如何使用反射满足工厂模式中的开闭原则?
Posted
技术标签:
【中文标题】如何使用反射满足工厂模式中的开闭原则?【英文标题】:How to satisfy Open Closed Principle in Factory Pattern using Reflection? 【发布时间】:2012-10-14 12:55:01 【问题描述】:我正在尝试使用 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");
【问题讨论】:
你手里拿着这本书。怎么没有回答你的问题?它应该教你O/C原理,而不是让你好奇。 @Marko 我完成了工厂模式一章,但我无法解决。 :) 那是章末的问题吧? 我是这个 OO 设计模式的新手。所以,不明白。 郑重声明,我不太看好那本书。它的教学风格相当混乱,例子过于复杂。说实话,很难教授仅在相当复杂的程序中才开始有意义的东西。这些东西最好从经验中学习,并且在脑海中浮现基本想法会有所帮助,但您不会通过解决书籍示例来深入了解它们。 【参考方案1】:就目前而言,每次ChicagoPizzaStore
推出一种新型披萨(Pizza
的新子类),您都需要向具体的创建者方法createPizza(String item)
添加更多功能,以使披萨店能够能够制作这些类型的披萨。
如您所见,这违反了OCP。
下面是这个问题的两种解决方案。
1.在createPizza(String item)
中使用反射来动态创建比萨饼
此解决方案将要求您最后一次违反 OCP 原则,但是使用反射动态创建 Pizza
实例意味着 ChicagoPizzaStore
将在此更改之外,不再需要修改以支持未来的风格比萨饼。
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
类型,则从类路径中删除此类 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。
【讨论】:
如果你想删除一个旧披萨? @4imble 当现有产品更改或从目录中删除时,我认为从 OCP 的角度来看,工厂模式会崩溃,除非产品的定义和解决方案是动态的。在这一点上,我更新了反射示例以评论如何使用这种方法从菜单中删除披萨。【参考方案2】:使用 switch 语句会破坏 OC。 要解决这个问题,多态性可能是要走的路。 也许是一个抽象工厂?
或者工厂通常是错误的,您想使用构建器模式。 毕竟披萨就是披萨就是披萨。 所以你只需要以不同的方式构建它们。 就像使用 StringBuilder...
【讨论】:
【参考方案3】:这是一个运行示例
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】:工厂方法设计模式的重点是使客户端和具体产品松散耦合。客户端仅与接口或基类交互。因此,将来如果您需要添加新的具体产品类(在您的情况下为 Pizza),新类不应导致客户端代码的更改(在您的情况下为 PizzaTestDrive
)。要添加新产品(Pizza),您只需要修改 Concrete Factory 类(在您的情况下为 ChicagoPizzaStore
)。
我认为您对工厂方法设计模式的实现是正确的。对于添加新的 Pizza,客户端代码不会改变,只有 Concrete Factory 类在改变。
【讨论】:
“你应该只需要修改 Concrete Factory 类” - OP 的问题是找到一种方法来扩展 ChicagoPizzaStore 的功能而不对其进行任何编码更改(这将违反 OCP)。我不相信你已经回答了 OP 的问题。【参考方案5】:如果你使用反射来满足开闭原则,你就是在牺牲性能。相反,您可以使用其他简单的技术按照开闭原则来制作您的工厂。 Factory Design Patterns and Open-Closed Principle (OCP), the ‘O’ in SOLID 对此给出了更恰当的解释。文章还讲述了如何调整简单工厂以遵循开放封闭原则。
【讨论】:
有两件事要提。第一件事:反射的性能与 OP 的问题无关——它是所描述问题的有效解决方案。第二件事:您提供了一个详细说明“其他简单技术”的链接,但您的链接使用的 SimpleFactory 问题的解决方案实际上是反射。【参考方案6】:在您的情况下使用反射并不是很好。最好使用属性文件之类的东西 ChicagoPizzaStore 将项目映射到班级... 例如:
cheese=ChicagoStyleCheesePizza
veggie=ChicagoStyleVeggiePizza
clam=ChicagoStyleClamPizza
【讨论】:
以上是关于如何使用反射满足工厂模式中的开闭原则?的主要内容,如果未能解决你的问题,请参考以下文章