1

At the moment my code looks like this. I have simplified the UML so it does not contain unnecessary information. I have used abstract factory design pattern.

enter image description here

My job is to create shapes. The problem is that these shapes are really complex to create. ShapeFactory has to know multiple different values to be able to create a shape. Because of that I have created ObjA which contains the necessary data to create the shape. This creates the problem that the modules are linked together by ObjA.

In examples like here the make methods parameter is a String but to me in this case it seems to be hard to replace the ObjA with a String because the String should contain about 50 different values.

Should I use AbstractFactory design pattern in this kind of a problem? Am I correct that using ObjA as a parameter is against the idea of using abstract factory design pattern? Is there a solution where I can keep ObjA and also keep the modules seperated?

(I am using Java and android studio in my project)

  • According to your UML ObjA doesn’t even know Polygon exists. Not sure what it’s even for. It seems to contain no data at all. – candied_orange Dec 21 '20 at 19:54
  • ObjA contains data. ShapeFactory uses this data to create the shape. For example ObjA contains locations and angles. The UML is simplied because i did not see a reason to write down what the ObjA really contains. – Matias Lappalainen Dec 21 '20 at 20:02
  • well this is too simple. Especially with a poor name like ObjA. I still don’t see how ObjA relates to Polygon. I just see ObjA go in and some random Shape coming out. – candied_orange Dec 21 '20 at 20:09
  • I am more interested about the principle. When I have looked at examples about abstract factories the input is usually something simple like a String for example "Circle". In my case the shape is not so simple because it contains hundreds of points. To create this shape I input an object (ObjA) which contains data. My understanding is that I should not input an ObjA into ShapeFactory because this makes the App side and the Factory side both depend on ObjA and they cannot be independly developed. Am I just understanding something compelete wrong? – Matias Lappalainen Dec 21 '20 at 20:25
2

Should I use AbstractFactory design pattern in this kind of a problem?

Depends on if the problem you have is the kind of problem that the Abstract Factory pattern provides a solution for. So let's look at that, and then, hopefully, you'll be able to decide for yourself.

An abstract factory is essentially a glorified collection of constructors that you can override. It provides a number of related factory methods for creating a family1 of objects, without requiring client code to know the concrete types of those objects - those types are set or configured somewhere else.


1 "family of objects" is just a fancy name for a "set of objects"; the word is used for style or to emphasize relatedness.


So, in the general case, there isn't just a single makeShape method. As described in the Design Patterns book, there are several methods, all of which return an abstraction of some product:

Abstract Factory Pattern

Am I correct that using ObjA as a parameter is against the idea of using abstract factory design pattern?

No! The pattern doesn't specify at all what the parameters to the creator methods should be - that's up to you (and the way you represented what you're trying to do in code).

There are different variations of the pattern; the example you've referenced uses a type-discriminating string to replace all the methods in the above diagram with a single method that dispatches on that parameter:

abstract Shape getShape(String shapeType)

But a) that's not the point of the pattern, and b) they can only do that because none of the concrete shapes require any parameters to be created. In general, each creator method can have it's own set of parameters - it all depends on how client code is going to use the abstract factory.

E.g., the sample code in Design Patterns provides this abstract factory:

public abstract class MazeFactory {
   abstract Maze makeMaze();
   abstract Wall makeWall();
   abstract Room makeRoom(int roomNumber);
   abstract Door makeDoor(Room roomA, Room roomB);
}

The idea is that there's a need to introduce a different set of subclasses of Maze, Wall, Room & Door that need to work together. However, client code that invokes the creation of each object shouldn't worry about mixing up incompatible subclasses. The abstract MazeFactory takes on this responsibility, letting the client code to change from:

Maze createMaze() {
  Maze maze = new Maze();
  Room r1 = new Room(1);
  Room r2 = new Room(2);
  // ...

  Door door = new Door(r1, r2);
  //...

  maze.AddRoom(r1);
  maze.AddRoom(r2);
  //...

  return maze;
}

to:

// 'factory' is instantiated elsewhere - e.g. in main()

Maze createMaze(MazeFactory factory) {
  Maze maze = factory.makeMaze();
  Room r1 = factory.makeRoom(1);
  Room r2 = factory.makeRoom(2);
  // ...

  Door door = factory.makeDoor(r1, r2);
  //...

  maze.AddRoom(r1);
  maze.AddRoom(r2);
  //...

  return maze;
}

Patterns aren't about the exact structure. They are about a general approach to solving a particular class of problems - details of the structure can vary. The pattern doesn't even require you to have subclasses of the abstract factory. The factory is abstract in the sense that it provides a programmer-defined abstraction for creating objects. It can, for example, be a concrete class that offers "slots" where you can plug in prototypical instances of each product, and then each creation method would return a copy of the prototypical instance on demand (perhaps modifying some of its properties first). This is also discussed in Design Patterns as a way to avoid combinatorial explosion of factory subclasses.

This creates the problem that the modules are linked together by ObjA.

ObjA is not the problem in itself. Every concrete factory is necessarily linked together (more precisely, depends on) the module that contains the concrete types of the products. This may be less obvious if you're using something like reflection, relying on type names to "magically" instantiate concrete objects (because the concrete implementation of your abstract factory is essentially provided by some other mechanism), but just try changing the name of some concrete type and you'll see that the whole thing breaks.

This is not where the decoupling happens. The concrete factory and concrete products go together. The decoupling happens at the abstract/concrete boundary, something like this:

enter image description here

What's decoupled here is (core high-level logic) vs (implementation of variation 1) vs (implementation of variation 2). You can write the high-level logic without worrying about implementation details of a variation, and you can easily reconfigure your app to use a different variation. You can also provide a new variation by deriving a new family of subclasses, without changing the high-level code. Each variation is essentially like a plugin.

If your 'ObjA' is a part of the abstract factory's interface, then it needs to be in the abstract ("high-level logic") part. Otherwise, if you're using the approach based on the prototypical instances, you may pass 'ObjA' to the concrete constructor of the prototype. A variation of that is: instead of prototypical instances, plug in lambdas that close over 'ObjA' and potentially take extra parameters to create the final product.

Now, what remains is to reconsider your problem in this light and see if it has this "shape", to put it that way. If not, then this may not be the right pattern.

1

Am I correct that using ObjA as a parameter is against the idea of using abstract factory design pattern?

No, you are not correct. A factory (or abstract factory) keeps being a factory independent of how the input data for the object construction is provided. If it is provided as a string, a filename, an ID to a database record or - as in this case - some kind of DTO does not matter.

This creates the problem that the modules are linked together by ObjA

The definition of ObjA should be placed in the same module as AbstractFactory. Then no additional module dependencies will be introduced. Each concrete factory will depend on the module where AbstractFactory (and ObjA) lives, that would be the same when using strings instead. AbstractFactory provides an interface for derived factory classes - try to interpret ObjA as part of this interface.

The only issue I see here is that there is a certain risk for getting requirements on ObjA to change with each new kind of shape introduced afterwards, which is actually what you want to prevent with this pattern. But that is impossible to assess without knowing more about ObjA and the actual shapes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.