我正在尝试使用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");
}
}
答案 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