一、简单工厂
简单工厂模式:书中提到简单工程其实并没有独立为一个“模式”,只是一个编程习惯。用的人比较多了,就总结出来了。
理解:其实这和我们现实当中的建立工厂生成是一样的。比如手机生产车间,需要组装一款手机,但是由很多型号,不同的型号配置不同的配件,不同的配件可以归类,归类其实就是归类到简单工厂里面。同类配件不同型号由一种工厂生产,比如芯片;而另一类配件由另一个工厂生产。这些配件被生产出来后送到组装车间。组装的流程是不变的,都是再组装同一个牌子的手机。仅仅是将变化的对象抽取到一个“工厂”去生产。
案例:PizzaStore
需求:现在有一家pizza店,由于门店扩张,每个店面招师傅做pizza的话无法保证口味,于是店主想把师傅集中起来,简单建一个pizza专门做pizza,送到门店销售。
思考:抽象一个Pizza类,定义好做pizza的方法
/**
* Project <demo-project>
* Created by jorgezhong on 2018/9/20 8:56.
* <p>
* Pizza定义了Pizza的制作工序
*/
public abstract class Pizza {
public void prepare() {
System.out.println("准备材料");
}
public void bake() {
System.out.println("烤");
}
public void cut() {
System.out.println("切");
}
public void box() {
System.out.println("包装");
}
}
public class CheesePizza extends Pizza {
@Override
public void prepare() {
super.prepare();
System.out.println("prepare cheese");
}
}
public class ClamPizza extends Pizza {
@Override
public void prepare() {
super.prepare();
System.out.println("prepare clam");
}
}
public class PepperoniPizza extends Pizza {
@Override
public void prepare() {
super.prepare();
System.out.println("prepare pepperoni");
}
}
public class VeggiePizza extends Pizza {
@Override
public void prepare() {
super.prepare();
System.out.println("prepare vegetable");
}
}
/**
* Project <demo-project>
* Created by jorgezhong on 2018/9/20 9:03.
* 定义工厂
*/
public class SimplePizzaFactory {
public Pizza createPizza(String type) {
Pizza pizza = null;
if ("cheese".equals(type)) {
pizza = new CheesePizza();
} else if ("pepperoni".equals(type)) {
pizza = new PepperoniPizza();
} else if ("clam".equals(type)) {
pizza = new ClamPizza();
} else if ("veggie".equals(type)) {
pizza = new VeggiePizza();
}
return pizza;
}
}
/**
* Project <demo-project>
* Created by jorgezhong on 2018/9/20 9:01.
*/
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;
}
}
总结:简单工程仅仅是抽象实例化对象的过程的代码而已。参考了人类工业化过程的做法。但是这个工厂可以有很多,方便拓展。由于独立于门店,披萨的生产可以拓展,多元化生产不同的产品,门店也可以多元化经营。后续门店拓展和产品拓展的时候就很方便了。比如西面的需求
需求:现在pizza扩张门店了,在芝加哥和纽约开发开分店。但是过了一段时间发现卖不出去,经过市场调研,改良了符合当地口味的pizza。
思考:这不正好吗,我开两家工厂生产符合当地口味的不就完了,门店所有的其他订购流程都不需要变。只需要提供不同工厂生产的pizza就可以。
/**
* Project <demo-project>
* Created by jorgezhong on 2018/9/20 9:03.
* 芝加哥工厂
*/
public class ChicagoPizzaFactory extends SimplePizzaFactory{
@Override
public Pizza createPizza(String type) {
Pizza pizza = null;
if ("cheese".equals(type)) {
pizza = new ChicagoStyleCheesePizza();
} else if ("pepperoni".equals(type)) {
pizza = new ChicagoPepperoniPizza();
} else if ("clam".equals(type)) {
pizza = new ChicagoClamPizza();
} else if ("veggie".equals(type)) {
pizza = new ChicagoVeggiePizza();
}
return pizza;
}
}
/**
* Project <demo-project>
* Created by jorgezhong on 2018/9/20 9:03.
* 纽约工厂
*/
public class NYPizzaFactory extends SimplePizzaFactory{
@Override
public Pizza createPizza(String type) {
Pizza pizza = null;
if ("cheese".equals(type)) {
pizza = new NYStyleCheesePizza();
} else if ("pepperoni".equals(type)) {
pizza = new NYPepperoniPizza();
} else if ("clam".equals(type)) {
pizza = new NYClamPizza();
} else if ("veggie".equals(type)) {
pizza = new NYVeggiePizza();
}
return pizza;
}
}
/**
* Project <demo-project>
* Created by jorgezhong on 2018/9/20 10:23.
*/
public class ChicagoStylePizzaStore {
SimplePizzaFactory factory;
public ChicagoStylePizzaStore(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;
}
}
/**
* Project <demo-project>
* Created by jorgezhong on 2018/9/20 10:23.
*/
public class NYStylePizzaStore {
SimplePizzaFactory factory;
public NYStylePizzaStore(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;
}
}
使用工厂制造不同风味的pizza
public class PizzaStoreDrive {
public static void main(String[] args) {
NYStylePizzaStore nyStylePizzaStore = new NYStylePizzaStore(new NYPizzaFactory());
nyStylePizzaStore.orderPizza("cheese");
nyStylePizzaStore.orderPizza("clam");
ChicagoStylePizzaStore chicagoStylePizzaStore = new ChicagoStylePizzaStore(new ChicagoPizzaFactory());
chicagoStylePizzaStore.orderPizza("cheese");
chicagoStylePizzaStore.orderPizza("clam");
}
}
总结:总感觉哪里不对是吧。感觉规划好了工厂都是简单工厂的模式,生产的pizza满足需求了,但是门店是不是太灵活了店。如果能好好管理门店就好了。
二、工厂方法
工厂方法模式:通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。该模式定义了一个创建对象的接口,但是由子类决定实例化的对象。工厂方法让类把实例化的动作推辞到子类。其实就是抽象的运用了。
需求:现在pizza店需要的很厉害了,不好管理,出现了各地师傅烘焙方法和流程不一样的情况,味道变化很大。需要各地门店按照总店的烘焙方法和包装方式来。
思考:我们发现决定当地风味的只有当地的门店和送到门店的工厂,也就是要由下面的门店来决定要哪个工厂生产的pizza。同时每个门店需要按照总店的加工流程来加工送过来的pizza。这样我们把变化的部分抽象出来就好了,不变的由子类继承即可。
1、将制造pizza这个动作从订购pizza抽象出来,甚至订购pizza的方法可以配置成final
/**
* Project <demo-project>
* Created by jorgezhong on 2018/9/20 9:01.
*/
public abstract class PizzaStore {
SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory) {
this.factory = factory;
}
public Pizza orderPizza(String type) {
//工厂生产需要的pizza
Pizza pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
/**
* 抽象工厂方法
*
* @param type
* @return
*/
abstract Pizza createPizza(String type);
}
public class ChicagoStylePizzaStore extends PizzaStore {
public ChicagoStylePizzaStore(SimplePizzaFactory factory) {
super(factory);
}
@Override
Pizza createPizza(String type) {
return factory.createPizza(type);
}
}
public class NYStylePizzaStore extends PizzaStore {
public NYStylePizzaStore(SimplePizzaFactory factory) {
super(factory);
}
@Override
Pizza createPizza(String type) {
return factory.createPizza(type);
}
}
关键在于抽象工厂方法:我们看下图关键代码对比一下,将factory.createPizza()抽象出来,可以由子类去决定由返回的pizza。本来由门店的订购流程决定的,由工厂生产pizza编程由下面门店决定用哪个工厂生产的pizza,而由总店决定订购流程。注意:这里抽象的是工厂的方法,就是原来工厂调用方法抽象出来了一层。下沉到子类。
总结:到这里我们其实已经看到了无论式pizza店还是pizza他们是创造和被创造的关系。由于拓展的需要,引入了第三方factory协调。分离了pizza店的创造职能。仔细观察我们的拓展方式,我们要拓展很多门店,同时要拓展很多pizza。因此这两者也是平级的拓展关系。简单工厂模式是简单的分离创造者(PizzaStore)的职能,重产品(Pizza)的生产,忽略了创造者的管理。而工厂方法模式是补充了简单工厂的缺点,在创造者身上开一个接口,抽象一层接口(createPizza方法)出来,让子类去实现。达到了管理创造者的效果。
核心思想:抽象的灵活运用,从简单工厂和工厂方法,体会到了抽象代码运用的妙处,可将代码抽象出一个角色去执行这段代码达到“协调”的效果,也可以抽象一段代码到方法,下沉代码,达到“管理”的效果。这里其实我们引入了一个设计原则
依赖倒置原则:依赖抽象,而不依赖具体
所谓倒置也是从具体到抽象的抽象方式,从底层开始往上抽象,而不是从顶层开始往下抽象。
三、抽象工厂
抽象工厂模式:小编理解的其实就是提供一个接口,定义对象的家族,抽象管理起来。并运用工厂方法来决定具体对象的创建,与工厂方法不同的是,抽象工厂更偏向于“组合”,定义对象家族范围更大。
需求:现在pizza店多了,在根据门店需求制作不同地域风味的pizza的时候,原来是门店准备一下原材料即可。随着量的增多,原材料也非常多了,门店也需要吧这一部分交给“工厂”去做。
思考:原料有一系列种类,可能会有很多工厂,便于管理,抽象一个顶层工厂接口,用于定义提供原材料的接口,使用原材料工厂的pizza门店依赖顶层接口,便于管理,原材料工厂。门店依旧使用工厂方法的模式来让子类决定具体使用哪个工厂创造哪些原材料对象。
/**
* Project <demo-project>
* Created by jorgezhong on 2018/9/21 10:01.
*
* 顶层原材料工厂:
* 这里定义接口其实是使用了组合的思想,使用方法来组合实例化的原材料对象,其子类实现这些方法,实例化当地的具体产品需要的的原材料
*/
public interface PizzaIngredientFactory {
Dough createDough();
Sauce createSauce();
Cheese createCheese();
Veggies[] createVeggies();
Pepperoni createPepperoni();
Clams createClams();
}
/**
* Project <demo-project>
* Created by jorgezhong on 2018/9/21 10:10.
*
* 具体工厂
*/
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
@Override
public Dough createDough() {
return new ThinCrustDough();
}
@Override
public Sauce createSauce() {
return new MarinaraSauce();
}
@Override
public Cheese createCheese() {
return new ReggianoCheese();
}
@Override
public Veggies[] createVeggies() {
return new Veggies[]{new Garlic(), new Onion(), new Mushroom(), new RedPepper()};
}
@Override
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
@Override
public Clams createClams() {
return new FreshClams();
}
}
/**
* Project <demo-project>
* Created by jorgezhong on 2018/9/21 10:31.
*
* 顶层产品抽象类:pizza,依赖一系列原材料种类
*/
public abstract class Pizza {
String name;
Dough dough;
Sauce sauce;
Veggies veggies[];
Cheese cheese;
Pepperoni pepperoni;
Clams clams;
public abstract void prepare();
public void bake(){
System.out.println("bake for 25 minutes at 350");
}
public void cut() {
System.out.println("cutting the pizza into diagonal slices");
}
public void box() {
System.out.println("place pizza in official PizzaStore box");
}
public String getName() {
return name;
}
public Pizza setName(String name) {
this.name = name;
return this;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Pizza{");
sb.append("name='").append(name).append('\'');
sb.append(", dough=").append(dough);
sb.append(", sauce=").append(sauce);
sb.append(", veggies=").append(Arrays.toString(veggies));
sb.append(", cheese=").append(cheese);
sb.append(", pepperoni=").append(pepperoni);
sb.append(", clams=").append(clams);
sb.append('}');
return sb.toString();
}
}
/**
* Project <demo-project>
* Created by jorgezhong on 2018/9/21 11:24.
*
* 具体产品(product)的子接口决定使用什么工厂,并且组合哪些原材料
*/
public class ClamPizza extends Pizza {
PizzaIngredientFactory pizzaIngredientFactory;
public ClamPizza(PizzaIngredientFactory pizzaIngredientFactory) {
this.pizzaIngredientFactory = pizzaIngredientFactory;
}
@Override
public void prepare() {
System.out.println("preparing " + name);
//跟工厂要原材料
dough = pizzaIngredientFactory.createDough();
sauce = pizzaIngredientFactory.createSauce();
cheese = pizzaIngredientFactory.createCheese();
}
}
/**
* Project <demo-project>
* Created by jorgezhong on 2018/9/20 9:01.
*/
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
//工厂生产需要的pizza
Pizza pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
/**
* 抽象工厂方法
*
* @param type
* @return
*/
abstract Pizza createPizza(String type);
}
/**
* Project <demo-project>
* Created by jorgezhong on 2018/9/20 10:23.
* 注意:使用了工厂方法,不同的是,类型的判断原来是放在工厂里面做的,现在放到了门店里面族,工厂只负责组合具体对象即可
*/
public class NYStylePizzaStore extends PizzaStore {
@Override
Pizza createPizza(String type) {
Pizza pizza = null;
NYPizzaIngredientFactory factory = new NYPizzaIngredientFactory();
if ("cheese".equals(type)) {
pizza = new CheesePizza(factory);
pizza.setName("New York Style Cheese Pizza");
} else if ("pepperoni".equals(type)) {
pizza = new PepperoniPizza(factory);
pizza.setName("New York Style Pepperoni Pizza");
} else if ("clam".equals(type)) {
pizza = new ClamPizza(factory);
pizza.setName("New York Style clam Pizza");
} else if ("veggie".equals(type)) {
pizza = new VeggiePizza(factory);
pizza.setName("New York Style veggie Pizza");
}
return pizza;
}
}
测试:
public class Test {
public static void main(String[] args) {
NYStylePizzaStore nyStylePizzaStore = new NYStylePizzaStore();
Pizza cheese = nyStylePizzaStore.orderPizza("cheese");
}
}
结果:
备注:抽象工厂的案例省略了芝加哥具体工厂类,其他具体的pizza产品类,所有原材料类,以及芝加哥门店类。大家可根据下面画出的uml类图喝依赖关系图自行补充哦,实在太多了,因此不重要的就不贴出来了
备注:依赖关系比较多,图比较大,只能点开来才看的清楚啦
总结:
- 从简单工厂,到工厂方法,最后到抽象工厂,工厂诠释了封装、抽象的精髓。特别是抽象的运用。我们可以从把抽象理解成abstract关键子的阶段毕业了,抽象的理解并不仅仅是关键字那么的简单,更多的是层级的管理。
- 简单工厂:借鉴了实际工业生产的基本管理思维,是简单的抽象,将一部分抽象出来独立于另一部分,独立出来的部分可以面向更多的角色,做更多的事情。
- 工厂方法:诠释了抽象方法是可以,下沉代码,交由子类决定实际动作。是将代码封装到下层。
- 抽象方法:利用
面向抽象,不面向具体的设计原则
,进一步抽象工厂,提升为工厂族,工厂族自然对应了产品族。而使用产品的角色则可以使用工厂族来管理被生产的产品族,抽象集合工厂方法,将具体工厂使用下沉到了多个具体使用产品的角色,以决定具体的产品对象。这里貌似有点绕。但是详细看依赖他,会发现其中的精妙。