2

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

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");
    }
}
4

6 回答 6

9

就目前而言,每次ChicagoPizzaStore推出新类型的比萨(的新子类Pizza)时,您都需要向具体的创建者方法添加更多功能,createPizza(String item)以使比萨店能够创建这些类型的比萨。

如您所见,这违反了OCP

以下是针对此问题的两种解决方案。

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

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

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

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从菜单中删除了 of 的类型,则从类路径中删除此类 Pizza 的类定义将导致createPizza(item)折扣风味返回 null。

由于各种原因,对反射的使用有其批评者,但对反射的批评超出了这个问题的范围,而且它对于实施遵循 Open/Closed 的工厂的问题来说是一个完全有效的解决方案原则。

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。

于 2014-12-18T15:03:42.237 回答
0

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

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

于 2017-08-01T19:56:02.103 回答
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.");
    }
}
于 2020-06-28T10:59:44.640 回答
-1

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

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

于 2012-10-14T13:23:22.317 回答
-1

如果你使用反射来满足开闭原则,你就是在牺牲性能。相反,您可以使用其他简单的技术按照开闭原则来制作您的工厂。工厂设计模式和开闭原则 (OCP),SOLID 中的“O” 对此给出了更恰当的解释。文章还讲述了如何调整简单工厂以遵循开放封闭原则。

于 2013-06-07T05:30:46.517 回答
-2

在您的情况下使用反射并不是很好。最好使用 ChicagoPizzaStore 的属性文件之类的东西来将项目映射到类...例如:

cheese=ChicagoStyleCheesePizza
veggie=ChicagoStyleVeggiePizza
clam=ChicagoStyleClamPizza
于 2014-11-09T16:04:26.367 回答