Capturing objects from the real world
Now, let's forget about Xcode and Swift for a while. Imagine that we have to develop a new universal iOS app that targets the iPad, iPhone, and iPod touch devices. We will have different User Interfaces (UI)Â and User Experiences (UX)Â because these devices have diverse screen sizes and resolutions. However, no matter the device in which the app runs, it will have the same goal.
Imagine that Vanessa is a very popular YouTuber, painter, and craftswoman who usually uploads videos on a YouTube channel. She has more than a million followers, and one of her latest videos had a huge impact on social networking sites. In this video, she sketched basic shapes and then painted them with acrylic paint to build patterns. She worked with very attractive colors, and many famous Hollywood actresses uploaded pictures on Instagram sharing their creations with the technique demonstrated by Vanessa and with the revolutionary special colors developed by a specific acrylic paint manufacturer.
Obviously, the acrylic paint manufacturer wants to take full advantage of this situation, so he specifies the requirements for an app. The app must provide a set of predefined 2D shapes that the users can drag and drop in a document to build a pattern so that they can change both the 2D position and size. It is important to note that the shapes cannot intersect, and users cannot change the line widths because these are the basic requirements of the technique introduced by Vanessa. A user can select the desired line and fill colors for each shape. At any time, the user can tap a button, and the app must display a list of the acrylic paint tubes, bottles, or jars that the user must buy to paint the drawn pattern. Finally, the user can easily place an online order to request the suggested acrylic paint tubes, bottles, or jars. The app also generates a tutorial to explain to the user how to generate each of the final colors for the lines and fills by thinning the appropriate amount of acrylic paint with water, based on the colors that the user has specified.
The following figure shows an example of a pattern. Note that it is extremely simple to describe the objects that compose the pattern: four 2D shapes-specifically, two rectangles and two circles. If we measure the shapes, we would easily realize that they aren't two squares and two ellipses; they are two rectangles and two circles:
We can easily recognize the objects; we understand that the pattern is composed of many 2D geometric shapes. Now, let's focus on the core requirement for the app, which is calculating the required amounts of acrylic paint. We have to take into account the following data for each shape included in the pattern in order to calculate the amount of acrylic paint:
- The perimeter
- The area
- The line color
- The fill color
The app allows users to use a specific color for the line that draws the borders of each shape. Thus, we have to calculate the perimeter in order to use it as one of the values that will allow us to estimate the amount of acrylic paint that the user must buy to paint each shape's border. Then, we have to calculate the area to use it as one of the values that will allow us to estimate the amount of acrylic paint that the user must buy to fill each shape's area.
We have to start working on the backend code that calculates areas and perimeters. The app will follow Vanessa's guidelines to create the patterns, and it will only support the following six shapes:
- Squares
- Equilateral triangles
- Rectangles
- Circles
- Ellipses
- Regular hexagons
We can start writing Swift code-specifically, six functions that calculate the areas of the previously enumerated shapes and another six to calculate their perimeters. Note that we are talking about functions, and we stopped thinking about objects; therefore, we will face some problems with this path, which we will solve with an object-oriented approach from scratch.
For example, if we start thinking about functions to solve the problem, one possible solution is to code the following twelve functions to do the job:
calculatedSquareArea
calculatedEquilateralTriangleArea
calculatedRectangleArea
calculatedCircleArea
calculatedEllipseArea
calculatedRegularHexagonArea
calculatedSquarePerimeter
calculatedEquilateralTrianglePerimeter
calculatedRectanglePerimeter
calculatedCirclePerimeter
calculatedEllipsePerimeter
calculatedRegularHexagonPerimeter
Each of the previously enumerated functions has to receive the necessary parameters of each shape and return either its calculated area or perimeter. These functions do not have side effects, that is, they do not make changes to the arguments they receive and they just return the results of the calculated perimeters. Therefore, we use calculated
instead of calculate
as the first word for their names. This way, it will be easier for us to generate the object-oriented version as we will continue to follow the API design guidelines that Apple has provided for Swift 3.
Now, let's forget about functions for a bit. Let's recognize the real-world objects from the application's requirements that we were assigned. We have to calculate the areas and perimeters of six elements, which are six nouns in the requirements that represent real-life objects-specifically 2D shapes. Our list of real-world objects is exactly the same that Vanessa's specification uses to determine the shapes allowed to be used to create patterns. Take a look at the list:
- Squares
- Equilateral triangles
- Rectangles
- Circles
- Ellipses
- Regular hexagons
After recognizing the real-life objects, we can start designing our application by following an object-oriented paradigm. Instead of creating a set of functions that perform the required tasks, we can create software objects that represent the state and behavior of a square, equilateral triangle, rectangle, circle, ellipse, and regular hexagon. This way, the different objects mimic the real-world 2D shapes. We can work with the objects to specify the different attributes required to calculate the area and perimeter. Then, we can extend these objects to include the additional data required to calculate other required values, such as the quantity of acrylic paint required to paint the borders.
Now, let's move to the real world and think about each of the previously enumerated six shapes. Imagine that we have to draw each of the shapes on paper and calculate their areas and perimeters. After we draw each shape, which values will we use to calculate their areas and perimeters? Which formulae will we use?
Tip
We started working on an object-oriented design before we started coding, and therefore, we will work as if we didn't know many concepts of geometry. For example, we can easily generalize the formulae that we use to calculate the perimeters and areas of regular polygons. However, we will analyze the requirements in most cases; we still aren't experts on the subject, and we need to dive deeper into the subject before we can group classes and generalize their behavior.
The following figure shows a drawn square and the formulae that we will use to calculate the perimeter and area. We just need the length of a side, usually identified as a:
The following figure shows a drawn equilateral triangle and the formulae that we will use to calculate the perimeter and area. This type of triangle has equal sides, and the three internal angles are equal to 60 degrees. We just need the length of each side, usually identified as a:
The following figure shows a drawn rectangle and the formulae that we will use to calculate the perimeter and area. We need the width and height values:
The following figure shows a drawn circle and the formulae that we will use to calculate the perimeter and area. We just need the radius, usually identified as r:
The following figure shows a drawn ellipse and the formulae that we will use to calculate the perimeter and area. We need the semimajor axis (usually labeled as a) and semiminor axis (usually labeled as b) values:
The following figure shows a drawn regular hexagon and the formulae that we will use to calculate the perimeter and area. We just need the length of each side, usually labeled as a:
The following table summarizes the data required for each shape:
Shape |
Required data |
Square |
The length of a side |
Equilateral triangle |
The length of a side |
Rectangle |
The width and height |
Circle |
The radius |
Ellipse |
The semimajor and semiminor axes |
Regular hexagon |
The length of a side |
Each object that represents a specific shape encapsulates the required data that we identified. For example, an object that represents an ellipse will encapsulate the ellipse's semimajor and semiminor axes.
Tip
Data encapsulation is one of the major pillars of object-oriented programming.