Learning Object-Oriented Programming
上QQ阅读APP看书,第一时间看更新

Organizing the blueprints – classes

So far, our object-oriented solution includes four classes with their attributes and methods. However, if we take another look at these four classes, we would notice that all of them have the same two methods: CalculateArea and CalculatePerimeter. The code for the methods in each class is different, because each shape uses a different formula to calculate either the area or the perimeter. However, the declarations or the contracts for the methods are the same. Both methods have the same name, are always parameterless, and both return a floating-point value.

When we talked about the four classes, we said we were talking about four different geometrical shapes or simply, shapes. Thus, we can generalize the required behavior for the four shapes. The four shapes must declare the CalculateArea and CalculatePerimeter methods with the previously explained declarations. We can create a contract to make sure that the four classes provide the required behavior.

The contract will be a class named Shape, and it will generalize the requirements for the geometrical shapes in our application. The Shape class declares two parameterless methods that return a floating-point value: CalculateArea and CalculatePerimeter. Then, we can declare the four classes as subclasses of the Shape class that inherit these definitions, but provide the specific code for each of these methods.

Tip

We can define the Shape class as an abstract class, because we don't want to be able to create instances of this class. We want to be able to create instances of Square, Rectangle, Circle, or Ellipse. In this case, the Shape abstract class declares two abstract methods. We call CalculateArea and CalculatePerimeter abstract methods because the abstract class declares them without an implementation, that is, without code. The subclasses of Shape implement the methods because they provide code while maintaining the same method declarations specified in the Shape superclass. Abstraction and hierarchy are the two major pillars of object-oriented programming.

Object-oriented programming allows us to discover whether an object is an instance of a specific superclass. After we changed the organization of the four classes and they became subclasses of the Shape class, any instance of Square, Rectangle, Circle, or Ellipse is also an instance of the Shape class. In fact, it isn't difficult to explain the abstraction because we are telling the truth about the object-oriented model that represents the real world. It makes sense to say that a rectangle is indeed a shape, and therefore, an instance of a Rectangle class is a Shape class. An instance of a Rectangle class is both a Shape class (the superclass of the Rectangle class) and a Rectangle class (the class that we used to create the object).

When we were implementing the Ellipse class, we discovered a specific problem for this shape; there are many formulas that provide approximations of the perimeter value. Thus, it makes sense to add additional methods that calculate the perimeter using other formulas.

We can define the following two additional parameterless methods, that is, two methods without any parameter. These methods return a floating-point value to the Ellipse class to solve the specific problem of the ellipse shape. The following are the two methods:

  • CalculatePerimeterWithRamanujanII: This uses the second version of a formula developed by Srinivasa Aiyangar Ramanujan
  • CalculatePerimeterWithCantrell: This uses a formula proposed by David W. Cantrell

This way, the Ellipse class implements the methods specified in the Shape superclass. The Ellipse class also adds two specific methods that aren't included in any of the other subclasses of Shape.

The following diagram shows an updated version of the UML diagram with the abstract class, its four subclasses, their attributes, and their methods: