0

I understand that the main code uses the factory to return an abstract pointer of the object desired, but it doesn't change the heritability of classes.

Can you explain in which context does the factory pattern reduce coupling?

1
  • Nothing is decoupled in terms of syntax; any change to an interface requires changes to all classes implementing the interface. However, if the library providing the concrete class implementation is separately developed from the library instantiating the class (third party code), the instantiation is decoupled, and the third party implementation may even be dynamically loaded. Commented Apr 23, 2017 at 22:57

4 Answers 4

1

By using a factory, the calling code can ignore what the concrete implementation is. That decouples the calling code from the implementations of an interface (or whatever abstraction the factory returns). The calling code also can ignore what the rules are for creating the implementation. That decouples your calling code from that logic (and allows it to be reused).

3
  • Maybe my definition of decoupling is wrong, but I still don't get how returning the interface instead of the concrete class reduce the coupling since the information is transmitted by the same way. You're somehow receiving the right returned object? And also, in what way hiding the the logic allow it to be reused, like reusing it for the same interface? I think I'm lacking of concrete notions Commented Apr 18, 2017 at 22:24
  • @max - sure, the code gets the right object, but it doesn't know or care what its implementation is. That means the implementation can be changed without changing the consuming code - decoupling at its core. Commented Apr 18, 2017 at 22:59
  • @Max, there may be many different concrete classes that are potentially returned all via the one and same (abstract or base) class or interface return type declared by factory . Commented Apr 19, 2017 at 0:02
1

Let's say you have a base class Shape.

class Shape
{
   virtual ~Shape() = 0;
   virtual double getArea() const = 0;
};

And a function to construct a Shape object of the given type:

Shape* constructShape(std::string const& shapeType);

In the client code, you want to use:

Shape* shape = constructShape("Square");
if ( shape != nullptr )
{
   // Use shape
}
else
{
   // Deal with the error.
} 

You can implement constructShape as:

Shape* constructShape(std::string const& shapeType)
{
   if ( shapeType == "Square" )
   {
      return new Square;
   }
   else if ( shapeType == "Circle" )
   {
      return new Circle;
   }

   // ....
   // Similar code for other known shape types.
   // ...

   else
   {
      // Unknown shape type
      return nullptr;
   }
}

Here, constructShape is strongly coupled with the types of Shapes the application knows about. If a new sub-type of Shape is added to the application, constructShape needs to be updated to support its construction.

Now, change that to use a factory pattern.

ShapeFactory.h:

// Add the necessary #include lines
// and forward declaration lines.

class ShapeFactory
{
   public:

      static void registerFactory(std::string const& shapeType,
                                  ShapeFactory* factory);

      static Shape* constructObject(std::string const& shapeType);

      virtual Shape* build() = 0;
};

constructShape can be implemented using:

Shape* constructShape(std::string const& shapeType)
{
   return ShapeFactor::constructObject(shapeType);
}

ShapeFactory.cpp:

// Add the necessary #include lines

typedef std::map<std::string, ShapeFactory*> ShapeFactoryMap;

static ShapeFactoryMap& getShapeFactoryMap()
{
   static ShapeFactoryMap theMap;
   return theMap;
}

void ShapeFactory::registerFactory(std::string const& shapeType,
                                   ShapeFactory* factory)
{
   getShapeFactoryMap()[shapeType] = factory;
}

Shape* ShapeFactory::constructObject(std::string const& shapeType)
{
   ShapeFactory* factory = getShapeFactoryMap()[shapeType];
   if ( factory == nullptr )
   {
      return nullptr;
   }
   else
   {
      return factory->build();
   }
}

When Square is added to the application, you'll have to make sure that:

  1. You have a sub-type of ShapeFactory corresponding to Square.
  2. Register an instance of the ShapceFactory with the base class.
class SquareFactory : public ShapeFactory
{
   Shape* build() { return new Square; }
};

ShapeFactor::registerFactory("Square", new SquareFactory);

With this, constructShape and ShapeFactory know only about the base class Shape. They will work unchanged for any new sub-type of Shape as long as the steps described above for Square are followed for the new sub-type.

Update, in response to comment by OP

main can be as simple as:

int main()
{
   std::cout << "Enter type of object to construct: ";
   std::cin >> shapeType;
   Shape* shape = constructShape(shapeType);
   if ( shape != nullptr )
   {
      // Use shape
   }
   else
   {
      // Deal with the error.
   }
}
2
  • What the main will look like? Does your code allow an user to add a new shape without oppening it? Commented Apr 18, 2017 at 23:34
  • @Max, Added update. Commented Apr 19, 2017 at 0:36
0

To add to the other answers: the coupling that is eliminated it specifically:

  1. Direct references from client code to (potentially many different) concrete classes via constructor invocations, r.g. new operations.

  2. The general pattern of selecting the right concrete (sub) class and otherwise initializing the object.

  3. Even choosing whether to create a new object instance each time, vs. instead returning and reusing a previously created instance.

By abstracting all these creation details, the client is mostly then concerned with using the returned object's interface. Names of concrete classes, the number if them, even whether the same object instance is reusable is abstracted form the client.

0

Coupling is not about what happens at the machine level, it is about how much a human reading the code need to be concerned about.

You are correct a factory method returning an interface might still return exactly the same object as using the constructor directly. But by encapsulating the construction, the client might be shielded from a lot of implementation details.

Say you have an ILogger interface which have a few concrete implementation like FileLogger and DatabaseLogger. The ILogger interface abstracts away the differences. So when you use the ILogger interface you are decoupled from the knowledge about how the logger is actually implemented, even the actual object you use is an instance of one of the concrete implementations.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.