本章目录如下:
一、阶段一
二、阶段二
三、阶段三
四、java中的装饰者
五、模式问答
六、设计原则总结
我们不应该针对实现编程,但是每次使用new初始化时正是针对实现编程!这会造成耦合问题,因为初始化绑着具体类使代码脆弱、缺乏弹性。所以先驱们发明了工厂模式能使初始化从复杂的依赖中脱困。
本章需求是披萨店需要根据客户点的披萨名称生产不同的披萨,且总部要求所有加盟店制作流程一致,原料统一配发。变化为:创建对象时根据条件不同创建不同的对象。下面我们以代码的迭代演化过程为线索具体讲解工厂模式。
一、阶段一
通过 if 来判断生产何种披萨。代码如下:
Pizza orderPizza(String type){
Pizza pizza;
if (type . equals (“cheese")) {
pizza = new CheesePizza() ;
}
else if (type. equals ("greek"){
pizza = new GreekPizza() ;
}
else if (type .equals ("pepperoni"){
pizza = new PepperoniPizza() ;
}
pizza.prepare() ;
pizza.bake() ;
pizza.cut() ;
pizza.box() ;
return pizza;
}
这是最常规的做法,但是缺点很明显:不能应对披萨种类的增加、减少这两种变化,且变化发生时只能修改原有代码,没有符合“开放——关闭”原则。
二、阶段二
既然方案一不能应对变化,那么就要抽象、封装变化。下面我们通过简单工厂(静态工厂)改进程序。
简单工厂定义:把创建对象的代码块抽象出来单独封装。
简单工厂其实不是一个设计模式,反而比较像是一种编程习惯。但由于经常被使用,所以我们给她一个设计模式荣誉奖。
public class SimplePizzaFactory{
public Pizza createPizza(String type) {
Pizza pizza = null;
if (type. equals ("cheese")) {
pizza = new CheesePizza() ;
}
else if (type.equals ("pepperoni")) {
pizza = new PepperoniPizza() ;
}
else if (type .equals("clam")) {
pizza = new ClamPizza();
}
else if (type .equals ("veggie")) {
pizza = new VeggiePizza();
}
return pizza;
}
}
public class PizzaStore {
SimplePizzaFactory factory;
public PizzaStore (SimplePizzaFactory factory) {
this. factory = factory;
}
public Pizza orderPizza(String type) {
Pizza pizza;
pizza = factory.createPizza(type) ;
pizza.prepare() ;
pizza.bake() ;
pizza.cut() ;
pizza.box() ;
return pizza;
}
}
简单工厂(静态工厂)把变化抽象出来的好处:
1)、可以提供多处使用,避免了代码重复。
2)、使披萨的制作过程符合“开放——关闭”原则。
缺点:不能通过继承来改变创建方法的行为。
简单工厂实现的披萨生产类图如下:
三、阶段三
随着披萨店的扩张,披萨店拥有了很多不同地区的加盟店(如东北、北京、河南、广东),这些加盟店需要符合该地区口味的披萨,如果按照方案二,我们为四个地区分别创建四个简单工厂即可。但是事情往往不是这么简单的,因为为了规范管理,总店希望控制加盟店的披萨制作过程,这就不是方案二能实现的了,因为方案二中每个加盟店的拥有披萨制作过程的代码,可以修改。
在寻找新方案之前我们需要先思考需求中的变化和不变化。变化在于不同地区的披萨要符合各地区口味,不变化在于总店想要各加盟店的制作工艺相同。==>这是思考新方案的前提。就我目前了解:继承的作用是复用,即造就相同!不同目前我了解的有组合、观察者模式、装饰模式、覆写抽象方法!
说实话《Head First设计模式》这本书我已经看了两遍了,但是当我整理时我还是懵逼了,又忘记了什么是工厂模式。截止上一段都是昨天晚上整理的,今天还是没想起来什么是设计模式,突发灵感想到了现实中的工厂,我觉得理解现实中的工厂很有助于理解什么是工厂模式。如果把多个工厂看成一个整体,那么多个工厂肯定要遵守一套规范,但是每个工厂本身又有自己的特点;如果分析一家工厂,那么工厂中的不同产品线肯定要遵守一套规范(接口),但是每种产品线(子类)本身又有自己的特点。那么客户跟谁对接呢?当然是抽象的规范(接口)啊!
从上面这段话我将工厂模式定义为:工厂模式用来处理对象的创建,将各流水线/子工厂遵守的流程封装在抽象类中,将原料的生产以抽象方法的形式定义在抽象类中,各流水线/子工厂继承抽象类并实现生产原料的抽象方法。(为什么在各流水线/子工厂中实现生产原料的抽象方法?因为各流水线/子工厂要有自己的特色,如果没有自己的特色,那就是同一个工厂了)
工厂模式通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。书中对工厂模式的定义为:工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,工厂方法让类把实例化推迟到子类。所谓的“决定” ,并不是指模式允许子类本身在运行时做决定,而是指在编写创建者类时,不需要知道实际创建的产品是哪一个。选择了使用哪个子类,自然就决定子实际创建的产品是什么。
===================Pizza=================
import java.util.ArrayList;
public abstract class Pizza {
String name;
String dough;
String sauce;
ArrayList
toppings = new ArrayList ();
void prepare() {
System.out.println("Prepare " + name);
System.out.println("Tossing dough...");
System.out.println("Adding sauce...");
System.out.println("Adding toppings: ");
for (String topping : toppings) {
System.out.println(" " + topping);
}
}
void bake() {
System.out.println("Bake for 25 minutes at 350");
}
void cut() {
System.out.println("Cut the pizza into diagonal slices");
}
void box() {
System.out.println("Place pizza in official PizzaStore box");
}
public String getName() {
return name;
}
public String toString() {
StringBuffer display = new StringBuffer();
display.append("---- " + name + " ----\n");
display.append(dough + "\n");
display.append(sauce + "\n");
for (String topping : toppings) {
display.append(topping + "\n");
}
return display.toString();
}
}
===================子类 Pizza=================
public class ChicagoStylePepperoniPizza extends Pizza {
public ChicagoStylePepperoniPizza() {
name = "Chicago Style Pepperoni Pizza";
dough = "Extra Thick Crust Dough";
sauce = "Plum Tomato Sauce";
toppings.add("Shredded Mozzarella Cheese");
toppings.add("Black Olives");
toppings.add("Spinach");
toppings.add("Eggplant");
toppings.add("Sliced Pepperoni");
}
void cut() {
System.out.println("Cutting the pizza into square slices");
}
}
其他的Pizza子类参考源码,整理的有!
===================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;
}
}
===================子类 PizzaStore=================
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 if (item.equals("pepperoni")) {
return new ChicagoStylePepperoniPizza();
} else return null;
}
}
public class NYPizzaStore extends PizzaStore {
Pizza createPizza(String item) {
if (item.equals("cheese")) {
return new NYStyleCheesePizza();
} else if (item.equals("veggie")) {
return new NYStyleVeggiePizza();
} else if (item.equals("clam")) {
return new NYStyleClamPizza();
} else if (item.equals("pepperoni")) {
return new NYStylePepperoniPizza();
} else return null;
}
}
===================顾客点餐实例=================
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");
pizza = chicagoStore.orderPizza("cheese");
System.out.println("Joel ordered a " + pizza.getName() + "\n");
}
}
以上代码的类图如下:
由上面代码的具体类图得出工厂模式的通用类图如下:
注意:模式设计是针对当下环境的,所考虑的变化也符合当下环境。环境质变(比如披萨店扩张为全国连锁)一般不考虑为变化,因为环境质变一般需要经历很长一个周期,设计的初期就把预计的质变考虑进去是不明智的,不能达到投入最优化!所以阶段二已经解决了我们最上面的需求,这里是产生了新的需求!
五、模式问答
1、什么是解耦?
我的理解是解依赖!
2、问当只有一个ConcreteCreator的时候,工厂模式有什么优点?
尽管只有一个具体创建者,工厂模式依然很有用,因为它帮助我们将产品的“实现”从“使用” 中解耦。如果增加产品或者改变产品的实现,Creator并不会受到影响(因为Creator与任何ConcreteProduct之间都不是紧耦合)。
六、设计原则总结
设计原则1:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化 T个T的代码混在一起。该设计原则作用: “把会变化的部分取出并封装起来,以便以后可以轻易地改动或扩充此部分,而不影响不需要变化的其他部分”。
设计原则2:针对接口编程,而不是针对实现编程。===我的理解是这个原则的使用优先级排在“设计原则1:变化原则”之后,即该原则是一个在大模式已确定需要实现具体类时针对实现类采用的原则,针对范围比较小。
设计原则3:为了交互对象之间的松耦合设计而努力。总结为“多用组合少用继承”!目前我见过两种组合方式:实例作为另外实例的属性,如策略模式、装饰者模式;实例作为另外实例的集合属性的成员,如观察者模式!
设计原则4(开放—关闭原则):类应该对扩展开放,对修改关闭。该原则目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。这样的设计具有弹性,可以接受新的功能来应对需求的改变。