OO Basics, Interfaces (Shapes)

Exercise Table

Exercise

Points

Point: Constructor and Getter Methods

5

Point: Default Constructor

2

Point: Static Method: equal()

3

Point: operator==()

3

Point: Method: move()

3

Point: operator+=()

3

Point: Static Method: add()

3

Point: operator+()

3

Circle: Constructor

4

Circle: operator==()

2

Circle: Method: area()

2

Square: Constructor

4

Square: operator==()

2

Square: Method: area()

2

Interface: Circle And Square Is-A Shape

5

Interface: Can Compute Area Of Any Shape

4

Total

50

Setup, Exam Procedure

$ ./tests/cxx-exercises-shapes--suite
Running main() from googletest/googletest/src/gtest_main.cc
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from initial_failure
[ RUN      ] initial_failure.failit
tests/initial-failure.cpp:5: Failure
Failed
[  FAILED  ] initial_failure.failit (0 ms)
[----------] 1 test from initial_failure (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] initial_failure.failit

 1 FAILED TEST
  • Remove the initial failure (e.g. comment out in CMakeLists.txt)

  • Implement the test cases that follow, one by one

    • Put the required files (point.{h,cpp}, circle.{h,cpp}, square.{h,cpp}, shape.h) into the lib/ directory (and edit its CMakeLists.txt accordingly)

    • In the tests/ directory, only the CMakeLists.txt needs to be modified (to add test files)

    • No test file must be modified!

Point

Point: Constructor and Getter Methods

#include <point.h>
#include <gtest/gtest.h>

TEST(point_suite, ctor_and_getters)
{
    Point p(1,2);                  // <--- constructor that take x and y parameters

    ASSERT_EQ(p.x(), 1);           // <--- getter for x coordinate
    ASSERT_EQ(p.y(), 2);           // <--- getter for y coordinate
}

Getters won’t modify the objects (⟶ are usable on const Point objects)

#include <point.h>
#include <gtest/gtest.h>

TEST(point_suite, ctor_and_getters_const)
{
    const Point p(1,2);            // <--- unmodifiable object

    ASSERT_EQ(p.x(), 1);
    ASSERT_EQ(p.y(), 2);
}

Point: Default Constructor

#include <point.h>
#include <gtest/gtest.h>

TEST(point_suite, default_ctor)
{
    Point p;                       // <--- default ctor used to initialize object

    ASSERT_EQ(p.x(), 0);           // <--- not initializing a Point gives origin
    ASSERT_EQ(p.y(), 0);
}

Point: Static Method: equal()

  • A static method equal() compares two Point objects for equality

  • Try to pass parameters by reference

#include <point.h>
#include <gtest/gtest.h>

TEST(point_suite, equal_method)
{
    Point p1(1,2);
    Point p2(1,2);

    ASSERT_TRUE(Point::equal(p1, p2));

    Point p3(1,2);
    Point p4(2,3);

    ASSERT_FALSE(Point::equal(p3, p4));
}

The method must be usable with const Point objects:

#include <point.h>
#include <gtest/gtest.h>

TEST(point_suite, equal_method_const)
{
    const Point p1(1,2);           // <--- unmodifiable
    const Point p2(1,2);           // <--- unmodifiable

    ASSERT_TRUE(Point::equal(p1, p2));

    const Point p3(1,2);           // <--- unmodifiable
    const Point p4(2,3);           // <--- unmodifiable

    ASSERT_FALSE(Point::equal(p3, p4));
}

Point: operator==()

  • Overload the == operator to implement a more expressive way than to call the equal() method

  • Try to pass parameters by reference

#include <point.h>
#include <gtest/gtest.h>

TEST(point_suite, operator_equals)
{
    Point p1(1,2);
    Point p2(1,2);

    ASSERT_TRUE(p1 == p2);         // <--- operator==() called

    Point p3(1,2);
    Point p4(2,3);

    ASSERT_FALSE(p3 == p4);        // <--- operator==() called
}

The method must be usable with const Point objects:

#include <point.h>
#include <gtest/gtest.h>

TEST(point_suite, operator_equals_const)
{
    const Point p1(1,2);           // <--- unmodifiable
    const Point p2(1,2);           // <--- unmodifiable

    ASSERT_TRUE(p1 == p2);

    const Point p3(1,2);           // <--- unmodifiable
    const Point p4(2,3);           // <--- unmodifiable

    ASSERT_FALSE(p3 == p4);
}

Point: Method: move()

  • p.move(vector) modifies a Point object by vector

  • Try to pass parameters by reference

#include <point.h>
#include <gtest/gtest.h>

TEST(point_suite, move_method)
{
    Point p(1,2);
    const Point vector(2,3);       // <--- unmodifiable

    p.move(vector);                // <--- modifies p, but not vector

    ASSERT_EQ(p.x(), 3);
    ASSERT_EQ(p.y(), 5);
}

Point: operator+=()

  • Overload the += operator to implement a more expressive way than to call the equal() method

  • Try to pass parameters by reference

#include <point.h>
#include <gtest/gtest.h>

TEST(point_suite, operator_plus_equals)
{
    Point p(1,2);
    const Point vector(2,3);       // <--- unmodifiable

    p += vector;                   // <--- modifies p, but not vector

    ASSERT_EQ(p.x(), 3);
    ASSERT_EQ(p.y(), 5);
}

Point: Static Method: add()

  • The static add() method creates a new Point object from its two parameters

  • Try to pass parameters by reference

#include <point.h>
#include <gtest/gtest.h>

TEST(point_suite, add_method)
{
    Point p(1,2);
    Point vector(2,3);

    Point new_p = Point::add(p, vector);  // <--- creates new Point

    ASSERT_EQ(new_p.x(), 3);
    ASSERT_EQ(new_p.y(), 5);
}

The method must be usable with const Point objects (it does not modify its parameters):

#include <point.h>
#include <gtest/gtest.h>

TEST(point_suite, add_method_const)
{
    const Point p(1,2);            // <--- unmodifiable
    const Point vector(2,3);       // <--- unmodifiable

    Point new_p = Point::add(p, vector);

    ASSERT_EQ(new_p.x(), 3);
    ASSERT_EQ(new_p.y(), 5);
}

Point: operator+()

  • Overload the + operator to implement a more expressive way than to call the add() method

  • Try to pass parameters by reference

#include <point.h>
#include <gtest/gtest.h>

TEST(point_suite, operator_plus)
{
    Point p(1,2);
    Point vector(2,3);

    Point new_p = p + vector;      // <--- creates new Point

    ASSERT_EQ(new_p.x(), 3);
    ASSERT_EQ(new_p.y(), 5);
}

The operator must be usable with const Point objects (just like add(), it does not modify its parameters):

#include <point.h>
#include <gtest/gtest.h>

TEST(point_suite, operator_plus_const)
{
    const Point p(1,2);            // <--- unmodifiable
    const Point vector(2,3);       // <--- unmodifiable

    Point new_p = p + vector;

    ASSERT_EQ(new_p.x(), 3);
    ASSERT_EQ(new_p.y(), 5);
}

Circle

A square is defined by:

  • A Point, the center

  • Its radius

Circle: Constructor

#include <circle.h>
#include <gtest/gtest.h>

TEST(circle_suite, ctor_and_getters)
{
    const Circle c(Point(1,2), 5);

    Point center = c.center();
    int radius = c.radius();

    ASSERT_EQ(center.x(), 1);
    ASSERT_EQ(center.y(), 2);
    ASSERT_EQ(radius, 5);
}

Circle: operator==()

#include <circle.h>
#include <gtest/gtest.h>

// same, using operator==()
TEST(circle_suite, ctor_and_getters_using_operator_equals)
{
    Circle c(Point(1,2), 5);

    ASSERT_TRUE(c.center() == Point(1,2)); // <--- requires Point::operator==()
}

Circle: Method: area()

The area() method is used to calculate a Circle object’s … well … area.

#include <circle.h>
#include <gtest/gtest.h>

TEST(circle_suite, area)
{
    Circle c(Point(1,2), 5);

    double area = c.area();

    ASSERT_FLOAT_EQ(area, 78.53981634);
}

Computing the area of a shape does not modify the object:

#include <circle.h>
#include <gtest/gtest.h>

TEST(circle_suite, area_const)
{
    const Circle c(Point(1,2), 5); // <--- unmodifiable

    double area = c.area();

    ASSERT_FLOAT_EQ(area, 78.53981634);
}

Square

A square is defined by:

  • A Point, the bottom left corner

  • Its side length

Square: Constructor

#include <square.h>
#include <gtest/gtest.h>

TEST(square_suite, ctor_and_getters)
{
    const Square s(Point(1,2), 5); // <--- unmodifiable

    Point bottom_left = s.bottom_left();
    int side_length = s.side_length();

    ASSERT_EQ(bottom_left.x(), 1);
    ASSERT_EQ(bottom_left.y(), 2);
    ASSERT_EQ(side_length, 5);
}

Square: operator==()

#include <square.h>
#include <gtest/gtest.h>

// same, using operator==()
TEST(square_suite, ctor_and_getters_using_operator_equals)
{
    Square s(Point(1,2), 5);

    ASSERT_TRUE(s.bottom_left() == Point(1,2)); // <--- requires Point::operator==()
}

Square: Method: area()

#include <square.h>
#include <gtest/gtest.h>

TEST(square_suite, area)
{
    Square s(Point(1,2), 5);

    double area = s.area();

    ASSERT_FLOAT_EQ(area, 5*5);
}

Computing the area of a shape does not modify the object:

#include <square.h>
#include <gtest/gtest.h>

TEST(square_suite, area_const)
{
    const Square s(Point(1,2), 5); // <--- unmodifiable

    double area = s.area();

    ASSERT_FLOAT_EQ(area, 5*5);
}

Interface: Shape

Although Circle and Square are shapes - both have an area() method -, they cannot be used exchangeable. Lets change that situation by defining an interface.

Interface: Circle And Square Is-A Shape

#include <shape.h>
#include <circle.h>
#include <square.h>
#include <gtest/gtest.h>

TEST(shape_suite, circle_is_a_shape)
{
    Circle c(Point(1,2), 5);
    Shape* shape = &c;             // <--- Circle* converted to Shape* because Circle is-a Shape

    (void)shape; // avoid "unused" warning
}
#include <shape.h>
#include <circle.h>
#include <square.h>
#include <gtest/gtest.h>

TEST(shape_suite, square_is_a_shape)
{
    Square s(Point(1,2), 5);
    Shape* shape = &s;             // <--- Square* converted to Shape* because Square is-a Shape

    (void)shape; // avoid "unused" warning
}

Interface: Can Compute Area Of Any Shape

#include <shape.h>
#include <circle.h>
#include <square.h>
#include <gtest/gtest.h>

TEST(shape_suite, area_is_shape_functionality__circle)
{
    Circle c(Point(1,2), 5);
    Shape* shape = &c;

    double area = shape->area();   // <--- area() defined by*interface*

    ASSERT_FLOAT_EQ(area, 78.53981634);
}
#include <shape.h>
#include <circle.h>
#include <square.h>
#include <gtest/gtest.h>

TEST(shape_suite, area_is_shape_functionality__square)
{
    Square s(Point(1,2), 5);
    Shape* shape = &s;

    double area = shape->area();   // <--- area() defined by*interface*

    ASSERT_FLOAT_EQ(area, 5*5);
}