如果经常使用new
来实例化一些具体类,就会使代码的灵活性更差,这时应当考虑将变化的部分封装起来。
以披萨店举例。制作一个简单的需要实例化一个披萨,然后调用加工披萨的方法。
当存在许多类披萨时,实例化披萨的代码会逐渐繁琐,如果将这些代码放在订购披萨的方法orderPizza
中,就要不断地修改该方法,而且当某些披萨不再使用时又要将其从代码中删除。因此,该部分属于变化的部分。
将关于披萨种类的代码封装至一个方法createPizza
中。这使得代码的可复用性增强了,可以在其他方法中调用这个封装的方法来获取一个特定种类的披萨,而不必反复地编写这段代码。
当然,这也不是一个优秀的方法。当披萨的种类发生变化时,仍然需要反复修改createPizza
方法。
public class DependentPizzaStore {
public Pizza createPizza(String style, String type) {
Pizza pizza = null;
if (style.equals("NY")) {
if (type.equals("cheese")) {
pizza = new NYStyleCheesePizza();
}
} else if (style.equals("Chicago")) {
if (type.equals("cheese")) {
pizza = new ChicagoStyleCheesePizza();
}
} else {
System.out.println("Error: invalid type of pizza");
return null;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
上述代码既欠缺复用性,也欠缺可扩展性。可见依赖具体类不是一个好的选择。
可以发现,实际情况还包含不同风味的披萨,在此下层才是不同种类的披萨。因此,应当将披萨店的createPizza
方法委托给一个工厂类,工厂类根据需要生产不同的披萨。(简单工厂)
但是,这仍然存在一个问题,披萨店次数无法控制披萨工厂的生产过程,因为未必能获得真正需要的披萨。
应当将披萨店设计为抽象基类,令不同制作风格的披萨店作为其子类来控制不同风味的披萨的制作过程,即不同的子类各自重写抽象的createPizza
方法。
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza;
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
abstract Pizza createPizza(String type);
}
public class NYPizzaStore extends PizzaStore {
@Override
Pizza createPizza(String type) {
if (type.equals("cheese")) {
return new NYStyleCheesePizza();
} else return null;
}
}
现在,可以由分店控制披萨的生产制作流程,并且为抽象的披萨店分担了生产的负担。
此时,createPizza
即是工厂方法。披萨店是抽象创建者类,披萨分店是具体创建者类,披萨及其子类是产品类。
利用这种结构,抽象创建者类能获取抽象产品类某个子类的实例,但其使用抽象产品类的接口就对具体实例进行操作。同时,只有具体创建者类知晓如何生产具体的产品。
于是,成功将面向客户的创建者类与产品类解耦。
考虑到披萨分店也不应当亲自创建披萨,因为不同种类的披萨有不同种类的原料,披萨店不应当存有如此大量的原料。它们也应当有属于它们的原料工厂,每个分店从具体的原料工厂中获取不同原料制作的披萨,然后为其命名再返回给总店即可。
注意,此时披萨类需要维护一组在prepare
方法中会用到的原料,不同的披萨子类从分店处接受一个原料工厂的引用,从原料工厂处获取原料,然后在prepare
方法中使用这些原料生产披萨(为原料赋值)。
现在情况更为复杂。披萨分店不再是自己创建披萨(因为大量不同的披萨的加工工艺十分复杂而又程序化),而是将创建披萨的工作委派给原料工厂。不同的披萨分店联系不同的原料工厂,不同的原料工厂提供不同的披萨原料。
于是披萨分店也不要知晓披萨的具体原料了,只需要从原料工厂处获取原料,然后按披萨的配方进行准备即可。这进一步地解耦了更加细节的实现。
本质上,抽象工厂定义一个负责创建一组产品的接口,这个接口内的每个方法都负责创建一个具体产品,利用实现抽象工厂的子类来提供这些具体的做法。因此,可以自然地在抽象工厂中利用工厂方法。
那么,用抽象工厂和直接使用工厂方法有什么区别?
答:区别在于,抽象工厂将不同类产品之间的搭配确定了,而直接使用工厂方法可能会发生搭配错误。并且工厂方法的本质是继承/实现,通过子类对抽象工厂方法的实现来获取产品;而抽象工厂的本质是委派/组合,通过将创建工作完全委派给具体子类来获取产品。
原文:https://www.cnblogs.com/aries99c/p/12806028.html