Sunday, May 17, 2020

The Factory Design Pattern


One of the reoccurring topics that will be covered on everythingdev will be design patterns. Having a solid understanding of design pattern methodologies will not only lead to becoming more proficient in the OOP paradigm, it can also just make your life much easier when implementing certain modules within a software project.

Right now we’ll be covering the Factory Design Pattern.

Let’s say we’re writing a paint program which allows the user to draw some shapes. Right now we can draw rectangles and circles. We’ll create a ‘Shape’ class that can handle rectangles and circles:

public class Shape {
     private int width = 0;
     private int height = 0;
     private double radius = 0.0D;
     private Boolean isRectangle = false;
     private Point[] hexagonPoints = null;  


     public Shape(int width, int height) {    
          this.isRectangle = true;
          this.width = width;  
          this.height = height;
     } 

     public Shape(double radius) {
          this.radius = radius;
     }

     public void draw() {
         if(isRectangle)
               // handle drawing the rectangle on screen...
          } else {
               // otherwise it’s a circle, and handle drawing the circle on screen….
          }
      }
}

The shape class has two constructors: one that accepts width and height parameters, and another which accepts a radius parameter. If the first constructor is called, the object will represent a rectangle, if the second is called it will be a circle. Easy enough. 
 
Soon our software has become a huge hit and we receive a request to add support for another shape type: hexagons. Hexagons will have width and height properties like rectangles. We decide to recycle an existing constructor we have for rectangles, and simply add a Boolean ‘isHexagon’ parameter to distinguish between a rectangle or hexagon, and update our draw() method accordingly.

Now our shape class looks like this:

public class Shape {
     private int width = 0;
     private int height = 0;
     private double radius = 0.0D;
     private Boolean isRectangle = false;

     private Boolean isHexagon = false;
     private Point[] hexagonPoints = null;  


     public Shape(int wfaceidth, int height, Boolean isHexagon) { 
          if(isHexagon) {
                    this.isHexagon = true;
          }
          else {
                    this.isRectangle = true;
          }   

          this.width = width;  
          this.height = height;
     } 

     public Shape(double radius) {
          this.radius = radius;
     }

     public void draw() {
         if(isRectangle)
               // handle drawing the rectangle on screen...
          }
         else if(isHexagon) {
                  // draw hexagon
         }
          else {
               // otherwise it’s a circle, and handle drawing the circle on screen….
          }
      }
}

Couple of emerging problems with our Shape class:

  • It’s rapidly becoming messier (imagine what it’s going to look like as we add support for more and more distinct shapes).
  • The more functionality we have, the harder debugging specific shape functionality will get.
  • The onus is on the developer to juggle all the constructors and initialization parameters in their head to remember how to build each shape.

Here’s another issue: when we changed the first constructor to accommodate hexagons, it forced us to update references to that constructor. So if we have 17 different classes within our codebase that initializes a shape using that constructor, that’s 17 classes that need to be updated along with the change to our Shape class. In other words, those 17 classes are overly reliant on being aware of the underlying implementation details of the shape class. We can also say that these classes are tightly coupled. We’ll solve these problems by implementing a factory class to handle creation of the shape objects instead.

We’ll start by creating a ‘Shape’ interface containing some properties and methods that are common to all Shapes regardless of their specific type:

public interface Shape {
                public void draw();
                public void getShapeType();
                public void setColor();
}

And we’ll refractor the Shape classes so they all implement our Shape interface:

class Rectangle implements Shape
                public void draw() {…….}
                public void getShapeType(){…….}
                public void setColor(){…….}
}

class Circle implements Shape
               
public void draw() {…….}
                public void getShapeType(){…….}
                public void setColor(){…….}
}

class Hexagon implements Shape
public void draw() {…….}
            public void getShapeType(){…….}
            public void setColor(){…….}
}

Then we’ll build a Factory class that will handle the creation of the various shapes:

public class ShapeFactory {

                final int TYPE_CIRCLE = 0;
                final int TYPE_OVAL = 2;
                final int TYPE_RECTANGLE = 3;
                final int TYPE_HEXAGON = 4;

                public Shape buildShape(int shapeType) { 
switch(shapeType):
case TYPE_CIRCLE: return new Circle(double radius)
                                // do stuff to make circles…
case TYPE_RECTANGLE: return new Rectangle(int width, int height)
                                // do stuff to make rectangles…
case TYPE_HEXAGON: return new Hexagon(int width, int height)
                                // do stuff to make hexagons…
default: return null;
}
                }
                return null;
}

Our Factory allows the developer (as well as other components within the system) to take a more hands-off approach to requesting certain shapes. Instead of getting caught up in the intricate under the hood details of a class (“It’s a rectangle not a hexagon… so I need to pass this parameter instead…”, or "Which constructor do I use for hexagons again?", etc) we simply make a request to our factory, “We want X type of shape”, pass the relevant information, and get back that type of shape. Then our other components can draw it, drag it, resize it, and do whatever else our program allows the user to do with shapes

The design of a Factory class could get much fancier. Having an enumerator that lists the shape types rather than those hardcoded integers is a good idea, for instance. Even in its current state though, our Factory class has already pushed us in the right direction to having a much cleaner design and loosely coupled system involving classes and objects that aren’t so heavily reliant on knowing each other’s implementation details.