简体   繁体   中英

Abstract Factory Pattern - Right way to implement it

Here's my code

public class FactoryPatternDemo {
public static void main(String[]args)
{
    AbstractFactory shapeFactory=new ShapeFactory();

    //tramite la fabbrica di figura geometrica disegno un rettangolo..
    Shape shape1=shapeFactory.getShape("rEcTaNgLe");
    shape1.draw();

    System.out.println();

    //..e un triangolo
    Shape shape2=shapeFactory.getShape("triangle");
    shape2.draw();
}

Shape Factory:

public class ShapeFactory extends AbstractFactory{

public ShapeFactory(){

}

@Override
public Shape getShape(String shapeType)
{
    if (shapeType==null)
            return null;
    if (shapeType.equalsIgnoreCase("RECTANGLE"))
            return new Rectangle();
    if (shapeType.equalsIgnoreCase("TRIANGLE"))
            return new Triangle();
    return null;
}

Abstract Factory:

public abstract class AbstractFactory {
public abstract Shape getShape(String shapeType);}

Abstract Product

public interface Shape {
void draw();}

Concrete Product#1

public class Rectangle implements Shape {

@Override
public void draw() {
    for(int i=0; i<5; i++)
    {
        if(i==0 || i==4)
        {
            for(int j=0; j<10; j++)
            {
                System.out.print("*");
            }
        }
        else
        {
            for(int j=0; j<10; j++)
            {
                if(j==0||j==9)
                    System.out.print("*");
                else
                    System.out.print(" ");
            }
        }
        System.out.print("\n");
    }

}

My question is: is this the right way to implement an Abstract Factory pattern? The client should be only able to see abstract things or interfaces in the FactoryPatternDemo class, but this line of code:

 AbstractFactory shapeFactory=new ShapeFactory();

shows the name of a concrete factory. Is this an error? Thanks guys

Well, for sure I would not use this design! It smells because of at least the following reasons:

  1. ShapeFactory.getShape() uses a kind of a switch, which is not Object Oriented Design.
  2. ShapeFactory is compile-time dependent on all your Shape objects.
  3. No-one cannot extend you library! Try to imagine, how someone who does not have control over your ShapeFactory want to add a new shape... Impossible!
  4. I cannot see any reason why AbstractFactory should be an abstract class and not an interface. It seems to me like a heritage from C++ rather than a Java design.

Have a look at the way how the java.sql.DriverManager.getConnection(connectionString) method is implemented. The best way is to watch the source code.

Very rough summary of the idea (it is hidden inside a lot of private methods). It is more or less an implementation of chain of responsibility, although there is not linked list of drivers.

DriverManager manages a list of drivers. Each driver must register itself to the DriverManager by calling its method registerDriver() . Upon request for a connection, the getConnection(connectionString) method sequentially calls the drivers passing them the connectionString. Each driver KNOWS if the given connection string is within its competence. If yes, it creates the connection and returns it. Otherwise the control is passed to the next driver. Analogy:

  • drivers: your concrete ShapeFactories
  • connection strings: types of your shapes
  • connection: an instance of a shape

I would try something like this:

public class ShapeManager {
    public void registerFactory(ShapeFactory factory) {
        // store the factory to the internal list
    }
    public shape getShape(String shapeType) {
        // go through the list of registered factories until one of them returns non-null
    }
}

public interface ShapeFactory {
    /**
     * Returns an instance of a shape, if shapeType is supported
     */
    public shape getShape(String shapeType);
}

public class TriangleFactory implements ShapeFactory {
    public static final String SHAPE_TYPE = "Triangle";
    @Override 
    public shape getShape(String shapeType) {
        if (SHAPE_TYPE.equals(shapeType) {
            return new Triangle();
        }
    }
}

public class RectangleFactory implements ShapeFactory {
    public static final String SHAPE_TYPE = "Rectangle";
    @Override 
    public shape getShape(String shapeType) {
        if (SHAPE_TYPE.equals(shapeType) {
            return new Triangle();
        }
    }
}

public class Client {
    ShapeFactory factory;

    // This is performed somehow on a global level, maybe by Spring configuration or so
    // However it is *not* part of the ShapeFactory so anyone may add their own shapes,
    // apart from those provided by default by your shape library.
    public void setUp() {
       factory = new ShapeFactory();
       factory.registerFactory(new TriangleFactory());
       factory.registerFactory(new RectangleFactory());
    }

    public void use() {
        final Shape triangle = factory.getShape("Triangle");
        final Shape rectangle = factory.getShape("Rectangle");
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM