OO Basics, Interfaces (Shapes)¶
Exercise Table¶
Exercise |
Points |
---|---|
5 |
|
2 |
|
3 |
|
3 |
|
3 |
|
3 |
|
3 |
|
3 |
|
4 |
|
2 |
|
2 |
|
4 |
|
2 |
|
2 |
|
5 |
|
4 |
|
Total |
50 |
Setup, Exam Procedure¶
See it fail initially
$ ./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 thelib/
directory (and edit itsCMakeLists.txt
accordingly)In the
tests/
directory, only theCMakeLists.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 twoPoint
objects for equalityTry 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 theequal()
methodTry 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 aPoint
object byvector
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 theequal()
methodTry 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 newPoint
object from its two parametersTry 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 theadd()
methodTry 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 centerIts 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 cornerIts 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);
}