如何用反射满足工厂模式中的开放闭合原理?

时间:2012-10-14 12:55:02

标签: java reflection factory-pattern open-closed-principle

我正在尝试使用Head First Design Pattern学习面向对象的设计模式。 这是本书中工厂模式的一个例子,我想在不违反开放封闭原则的情况下添加新的披萨项目。 在书中给出的示例代码中,如果我添加新的披萨项类,我需要修改PizzaStore和PizzaOrder类。但我只是想添加新的Pizza Item而不修改其他类。

public class ChicagoPizzaStore extends PizzaStore {

Pizza createPizza(String item) {
        if (item.equals("cheese")) {
                return new ChicagoStyleCheesePizza();
        } else if (item.equals("veggie")) {
                return new ChicagoStyleVeggiePizza();
        } else if (item.equals("clam")) {
                return new ChicagoStyleClamPizza();
        } 
            else return null;
}

}

这个pizzaStore课程是创建和订购披萨。

public abstract class PizzaStore {

    abstract Pizza createPizza(String item);

public Pizza orderPizza(String type) {
    Pizza pizza = createPizza(type);
    System.out.println("--- Making a " + pizza.getName() + " ---");
    pizza.prepare();
    pizza.bake();
    pizza.cut();
    pizza.box();
    return pizza;
}

}

这是抽象的Pizza课程:

public abstract class Pizza {
String name;
String dough;
String sauce;
ArrayList toppings = new ArrayList();

void prepare() {
    System.out.println("Preparing " + name);
    System.out.println("Tossing dough...");
    System.out.println("Adding sauce...");
    System.out.println("Adding toppings: ");
    for (int i = 0; i < toppings.size(); i++) {
        System.out.println("   " + toppings.get(i));
    }
}

此类用于接受客户的订单。

 public class PizzaTestDrive {

        public static void main(String[] args) {
            PizzaStore nyStore = new NYPizzaStore();
            PizzaStore chicagoStore = new ChicagoPizzaStore();

            Pizza pizza = nyStore.orderPizza("cheese");
            System.out.println("Ethan ordered a " + pizza.getName() + "\n");
    }
}

这是我的新披萨项目类。我想订购这个披萨项而不修改chicagoPizzaStore和testDrive类:

public class ChicagoStyleClamPizza extends Pizza {
    public ChicagoStyleClamPizza() {
        name = "Chicago Style Clam Pizza";
        dough = "Extra Thick Crust Dough";
        sauce = "Plum Tomato Sauce";
        toppings.add("Shredded Mozzarella Cheese");
        toppings.add("Frozen Clams from Chesapeake Bay");
    }

    void cut() {
        System.out.println("Cutting the pizza into square slices");
    }
}

6 个答案:

答案 0 :(得分:8)

按照目前的情况,每次ChicagoPizzaStore推出新类型的披萨(Pizza的新子类)时,您需要为具体的创建者方法添加更多功能createPizza(String item)使比萨商店能够创建这些类型的比萨饼。

如您所知,这违反了OCP

以下是这个问题的两种解决方案。

<强> 1。在createPizza(String item)中使用反射来动态创建比萨

此解决方案将要求您最后一次违反OCP原则,但使用反射动态创建Pizza个实例意味着ChicagoPizzaStore将超出此更改,不再需要修改为支持未来的比萨风味。

新类型Pizza类的名称必须与提供给create pizza方法的键(item参数)的名称相匹配。解决方案的工作原理如下:

public class ChicagoPizzaStore extends PizzaStore {

Pizza createPizza(String item) {
        try {
            //some assumptions about the classpath locations made here
            return Class.forName(item).newInstance();
        } catch(Exception e) {
            return null;
        }
}

创建新类型的Pizza时,可以简单地将这些类型作为createPizza(item)方法的键传递,然后创建它们。

同样,如果从菜单中删除Pizza类型,则从类路径中删除此类Pizza的类定义将导致createPizza(item)为折扣标记返回null。

由于各种原因,反思的使用受到了批评,但对反思的批评超出了这个问题的范围,而且对于实施坚持以下问题的工厂的问题而言,这是一个完全有效的解决办法。开放/封闭原则。

<强> 2。子类ChicagoPizzaStore

作为SOLID中的O,类是“开放式扩展并关闭以进行修改”。因此,解决问题的方法只是扩展ChicagoPizzaStore

public class ExtendedChicagoPizzaStore extends ChicagoPizzaStore {

    Pizza createPizza(String item) {
            if (item.equals("spicy")) {
                    return new RidiculouslySpicyPizza();
            } else {
                    return super.createPizza(item);
           }
    }

此解决方案的优点是不会违反OCP以便应用它。

答案 1 :(得分:0)

工厂方法设计模式的要点是使客户和具体产品松散耦合。客户端仅与接口或基类交互。因此,将来如果您需要添加新的具体产品类(在您的情况下为Pizza),新类不应导致客户端代码的更改(在您的情况下为PizzaTestDrive)。要添加新产品(Pizza),您只需要修改Concrete Factory类(在您的情况下为ChicagoPizzaStore)。

我认为您的Factory Method设计模式的实现是正确的。对于添加新的Pizza,客户端代码不会改变,只有Concrete Factory类正在改变。

答案 2 :(得分:0)

有一个switch语句破坏OC。 要解决这个问题,可能需要采用多态性。 也许是一个抽象的工厂?

或者工厂通常是错误的,并且您想要使用构建器模式。 毕竟比萨饼是披萨比萨饼。 所以你只需要以不同的方式构建它们。 和StringBuilder一样......

答案 3 :(得分:0)

这是一个正在运行的示例

class FactoryClosedForModification {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
            ShapeFactory sf = new ShapeFactory();
            Shape shape = (Shape) sf.getShape(Triangle.class.getName());
            shape.draw();
        }
    }


class ShapeFactory {

    public Object getShape(String shapeName)
            throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        return Class.forName(shapeName).newInstance();
    }
}

class Shape {
    public void draw() {
        System.out.println("Drawing a shape.");
    }
}

class Triangle extends Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a Triangle.");
    }
}

class Circle extends Shape {
    @Override
    public void draw() {
        System.out.println("Drawing Circle.");
    }
}

答案 4 :(得分:-1)

如果您使用反射来满足开放封闭原则,那么您就会影响性能。相反,您可以使用其他简单的技术,根据开闭原则制造您的工厂。 Factory Design Patterns and Open-Closed Principle (OCP), the ‘O’ in SOLID为此提供了更恰当的解释。文章还讲述了如何调整简单工厂以遵守开放封闭原则。

答案 5 :(得分:-2)

在你的情况下使用反射并不是很好。最好使用类似属性的文件 ChicagoPizzaStore来映射课堂上的项目...... 例如:

cheese=ChicagoStyleCheesePizza
veggie=ChicagoStyleVeggiePizza
clam=ChicagoStyleClamPizza