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

nir*_*ali 2 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;
}
Run Code Online (Sandbox Code Playgroud)

}

这个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;
}
Run Code Online (Sandbox Code Playgroud)

}

这是抽象的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));
    }
}
Run Code Online (Sandbox Code Playgroud)

此类用于从客户处获取订单.

 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");
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的新披萨项目类.我想订购这个披萨项而不修改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");
    }
}
Run Code Online (Sandbox Code Playgroud)

8bi*_*kie 8

就目前而言,每次ChicagoPizzaStore出现一种新型披萨(新的子类Pizza)时,您需要为具体的创建者方法添加更多功能,createPizza(String item)以使Pizza商店能够创建这些类型的披萨.

如您所知,这违反了OCP.

以下是该问题的两种解决方案.

1.使用内部反射createPizza(String item)动态创建比萨饼

此解决方案将要求您最后一次违反OCP的原则,但是使用反射动态创建Pizza实例意味着ChicagoPizzaStore,除了此更改之外,不再需要修改以支持未来的Pizza风味.

新类型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;
        }
}
Run Code Online (Sandbox Code Playgroud)

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

类似地,如果Pizza从菜单中取出一种类型,从类路径中删除这样一个Pizza的类定义将导致createPizza(item)为discountinued flavor返回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);
           }
    }
Run Code Online (Sandbox Code Playgroud)

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