在之前内容里,介绍了工厂模式中的简单工厂和工厂方法内容,这我们继续工厂模式的学习,今天学习抽象工厂模式。
当直接实例化一个对象时,就是在依赖它的具体类。看一张对象依赖图
很明显PizzaStore依赖所有披萨对象,所有的具体pizza都从PizzaStore当中产生。
当下面各个新增一种披萨种类,就相当于多了一个依赖,上面PizzaStore可能就要修改。
这里引出一个原则:依赖倒置原则。
注:要依赖抽象,不要依赖具体类。感觉有点像面向接口编程,而不面向实现编程。确实这很相似,然而这里更强调“抽象”.这个原则说明:不能让“高层组件”依赖“底层组件”,换句话说好比人要吃鸡鸭鱼肉等食物摄取营养,但是人不吃鸡鸭鱼肉也能从别的许多食物的摄取营养物质的把。所以二者不应该是依赖的关系,回过来说,“高层组件”和“底层组件”都要依赖抽象。
PizzaStore是“高层组件”,而各个披萨具体实现PizzaStore的具体实现类,就为“底层组件”,很明白,PizzaStore依赖这些具体比萨类。
我们把依赖倒置原则,放在我们目前的披萨店项目中。类图如下
想要遵循依赖倒置原则,(依赖抽象)工厂方式并不一定是最好的方法,但却是最有效方法之一。
何为“倒置”?
前面说“依赖”,现在来解释解释倒置原则,在上面当中我们看到各个pizza具体类(低层组件)向上指向了Pizza(高层组件),底层组件现在竟然依赖高层组件,对比图一当中对象(向下指向)依赖具体实现类情况,是不是指向都反过来了。所以说倒置指的就是和一般OO设计的思考方式完全相反。而现在我们的设计就让高层和底层组件都依赖Pizza这个抽象。
所以说若要遵循依赖倒置原则的话必须遵守下面几点要求。
1:变量不可以持有具体类的引用。
注:如果使用new,就会持有具体类的引用。你可以使用工厂模式避免这个问题。
2:不要让类派生自具体类。
注:如果派生自具体类,就会依赖这个具体类。请派生自一个抽象
3:不要覆盖基类中已经实现的方法。
编者注:包括我之前做的几期模式文章,当学习到工厂模式的时候我也有脑浆糊时候,倘若真的写项目程序之前都要想如何使用什么设计模式的话,恐怕我什么程序都写不出来,在设计模式中,无论有多少原则,其实是要尽量去达到,而不是说随时都要遵守原则或模式。其实很多时候任何Java程序在某种程度上来说都做不到百分之百遵守原则,都有违反方针地方,但设计模式可以作为一个项目的灵魂。
但是,如果你深入体验模式,将这些模式内化成你思考的一部分,那么在设计时,你将知道何时有足够的理由违反这些原则。就比方说当你实例化一个字符串的时候,你是不是直接实例化了?其实我们平时在程序中也都不假思索的实例化字符串对象吗?有违反这个原则吗?有!能这样做吗?能!为什么?因为字符串不能改变。包括我自己目前学习设计模式都还只是学“形”,我离设计模式的“心”还有不少的距离。
回到我们披萨店,现在看看我们披萨店“生意”是蒸蒸日上,具体弹性的框架,维护拓展起来也比最初设计的强很多,虽然花的功夫不少,但往往是值得的。
某一天你接到了好几个顾客的投诉电话,投诉的内容大体相同,某某加盟店里的食材不新鲜,甚至有媒体曝光你的某些加盟店为了降低成本,使用劣质原料。身为老板得你,必须采取一些办法,阻止这样的情况再次发生。
往后几天,你亲自考察了位于纽约和芝加哥的两家加盟店,经过研究决定为确保加盟店使用高质量的原料,你打算建造一家生产原料的工厂,并将原料运到各个加盟店专供,下面是你写的两份菜单
现在我们建造一个工厂,来生产原料,
定义工厂接口,负责创建原料
这里有很多新类,每个原料都是一个类(接口),每个原料都有个create方法,到时候我们都要具体展开。
下面具体创建纽约原料工厂,
每个原料都重载接口,根据不同风味定制添加不同的调料,对于蔬菜(Veggies),用一个数组做返回值,这里我就自己写死了数组,芝加哥工厂暂时不放出。
这里Pizza就需要修改了,好让它能使用工厂生产出来的原料。
下面是创建披萨,下面以芝士披萨为例,不在像之前那样做法了,写出区域性话,就单单创建某某味道的披萨
下面图将给你更好的说明(我就直接引用书中笔记了)
下面是修改加盟店代码以纽约店为例,纽约店会用到纽约披萨原料工厂,把工厂对象传入每个披萨里就能“获取”对应原料。
相比于之前的createPizza方法,这个版本两者之间有何异同呢?
我们目前做了这些。
这样的工厂形式就是抽象工厂模式
抽象工厂模式:提供一个接口,用于创建相关或依赖对象,而不需要明确指定具体类。
下面是抽象工厂类图
抽象方法,给我感觉是它更多运用抽象形式,简化编码当中复杂性。
下面是工厂方法和抽象工厂两种模式的对比
工厂方法:
抽象工厂:
其实,无论简单工厂,工厂方法还是抽象工厂方法,都对我们提升代码质量有很大帮助。
要点:
1:所有的工厂都是用来封装对象的创建
2:简单工厂,虽然不算是设计模式,但却是一个很好的编码习惯,可以使客户和具体类解耦
3:工厂方法使用继承:把对象的创建放在子类,子类实现工厂方法。
4:抽象工厂模式使用对象组合:对象的创建被是现在工厂的接口中。
5:所有的工厂模式都是减少程序中类之间依赖,松耦合。
6:第一次了解原来实例化可以延迟到子类中进行。
7:学习了依赖倒置原则。
8:面向抽象编程,不面向具体类编辑,尽管自己还是会犯老毛病。
下面放出实例的全部源码,由于源码类文件较多,先放出了项目class图
Material_Implements包下内容
披萨工厂接口
package Factory_Interface;
import Material_Interface.Cheese;
import Material_Interface.Clams;
import Material_Interface.Dough;
import Material_Interface.Pepperoni;
import Material_Interface.Sauce;
import Material_Interface.Veggies;
/**
* 这时我们需要给披萨店送原料了 建立一家生产原料的工厂 添加各种原料
*
* @author Joy
*
*/
public interface PizzaIngredientFactory {
// 面饼
public Dough createDough();
// 酱料
public Sauce createSauce();
// 芝士奶酪
public Cheese createCheese();
// 各种蔬菜,数组
public Veggies[] createVeggies();
// 香肠
public Pepperoni createPepperoni();
// 海鲜
public Clams createClam();
}
两个具体工厂实现
package Factory_Implements;
import Factory_Interface.PizzaIngredientFactory;
import Material_Implements.FreshClams;
import Material_Implements.Garlic;
import Material_Implements.MarinaraSauce;
import Material_Implements.Mushroom;
import Material_Implements.Onion;
import Material_Implements.RedPepper;
import Material_Implements.ReggianoCheese;
import Material_Implements.SlicedPepperoni;
import Material_Implements.ThinCrustDough;
import Material_Interface.Cheese;
import Material_Interface.Clams;
import Material_Interface.Dough;
import Material_Interface.Pepperoni;
import Material_Interface.Sauce;
import Material_Interface.Veggies;
/**
* 纽约披萨店原料工厂实现,工厂专精大蒜番茄酱料,干酪,鲜蛤蛎
*
* @author Joy
*
*/
// 具体的原料工厂都要实现PizzaIngredientFactory类
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() {
Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(),
new RedPepper() };
return veggies;
}
@Override
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
@Override
public Clams createClam() {
return new FreshClams();
}
}
package Factory_Implements;
import Factory_Interface.PizzaIngredientFactory;
import Material_Implements.BlackOlives;
import Material_Implements.Eggplant;
import Material_Implements.FrozenClams;
import Material_Implements.MozzarellaCheese;
import Material_Implements.PlumTomatoSauce;
import Material_Implements.SlicedPepperoni;
import Material_Implements.Spinach;
import Material_Implements.ThickCrustDough;
import Material_Interface.Cheese;
import Material_Interface.Clams;
import Material_Interface.Dough;
import Material_Interface.Pepperoni;
import Material_Interface.Sauce;
import Material_Interface.Veggies;
/**
* 加州披萨店原料工厂
*
* @author Joy
*
*/
public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory {
@Override
public Dough createDough() {
return new ThickCrustDough();
}
@Override
public Sauce createSauce() {
return new PlumTomatoSauce();
}
@Override
public Cheese createCheese() {
return new MozzarellaCheese();
}
@Override
public Veggies[] createVeggies() {
Veggies veggies[] = { new BlackOlives(), new Spinach(), new Eggplant() };
return veggies;
}
@Override
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
@Override
public Clams createClam() {
return new FrozenClams();
}
}
各个原料接口
package Material_Interface;
/**
* 芝士奶酪类
* @author Joy
*
*/
public interface Cheese {
//只需定义接口,具体实现让实现类搞定
public String toString();
}
package Material_Interface;
/**
* 海蛎类
* @author Joy
*
*/
public interface Clams {
public String toString();
}
package Material_Interface;
/**
* 面饼类
* @author Joy
*
*/
public interface Dough {
public String toString();
}
package Material_Interface;
/**
* 香肠类
* @author Joy
*
*/
public interface Pepperoni {
public String toString();
}
package Material_Interface;
/**
* 酱料类
* @author Joy
*
*/
public interface Sauce {
public String toString();
}
package Material_Interface;
/**
* 蔬菜类
* @author Joy
*
*/
public interface Veggies {
public String toString();
}
下面就是各个原料的具体实现
package Material_Implements;
import Material_Interface.Veggies;
public class BlackOlives implements Veggies {
public String toString() {
return "黑橄榄";
}
}
package Material_Implements;
import Material_Interface.Veggies;
public class Eggplant implements Veggies {
public String toString() {
return "茄子";
}
}
package Material_Implements;
import Material_Interface.Clams;
public class FreshClams implements Clams {
public String toString() {
return "鲜海蛎";
}
}
package Material_Implements;
import Material_Interface.Clams;
public class FrozenClams implements Clams {
public String toString() {
return "海湾的鲜海蛎";
}
}
package Material_Implements;
import Material_Interface.Veggies;
public class Garlic implements Veggies {
public String toString() {
return "大蒜";
}
}
package Material_Implements;
import Material_Interface.Sauce;
public class MarinaraSauce implements Sauce {
@Override
public String toString() {
return "番茄酱";
}
}
package Material_Implements;
import Material_Interface.Cheese;
public class MozzarellaCheese implements Cheese {
@Override
public String toString() {
return "碎芝士";
}
}
package Material_Implements;
import Material_Interface.Veggies;
public class Mushroom implements Veggies {
public String toString() {
return "香菇";
}
}
package Material_Implements;
import Material_Interface.Veggies;
public class Onion implements Veggies {
public String toString() {
return "洋葱";
}
}
package Material_Implements;
import Material_Interface.Sauce;
public class PlumTomatoSauce implements Sauce {
public String toString() {
return "梅子番茄酱";
}
}
package Material_Implements;
import Material_Interface.Veggies;
public class RedPepper implements Veggies {
public String toString() {
return "红辣椒";
}
}
package Material_Implements;
import Material_Interface.Cheese;
public class ReggianoCheese implements Cheese {
public String toString() {
return "干奶酪";
}
}
package Material_Implements;
import Material_Interface.Pepperoni;
public class SlicedPepperoni implements Pepperoni {
public String toString() {
return "香肠切片";
}
}
package Material_Implements;
import Material_Interface.Veggies;
public class Spinach implements Veggies {
public String toString() {
return "菠菜";
}
}
package Material_Implements;
import Material_Interface.Dough;
public class ThickCrustDough implements Dough {
public String toString() {
return "厚面饼";
}
}
package Material_Implements;
import Material_Interface.Dough;
public class ThinCrustDough implements Dough {
public String toString() {
return "薄面饼";
}
}
下面是Pizza“工厂”(抽象)方法
package Pizza;
import Material_Interface.Cheese;
import Material_Interface.Clams;
import Material_Interface.Dough;
import Material_Interface.Pepperoni;
import Material_Interface.Sauce;
import Material_Interface.Veggies;
public abstract class Pizza {
String name;// 披萨名称
Dough dough;// 面团类型
Sauce sauce;// 酱料
// ArrayList toppings = new ArrayList();// 一套佐料(下面原料代替了list)
// 每个披萨都持有一组需用到的原料
Veggies veggies[];
Cheese cheese;
Pepperoni pepperoni;
Clams clams;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
/**
* //把prepare方法抽象化,获取来自原料工厂的原料 Pizza类完全不关心具体原料是什么,只知道如何制作披萨,
* 比萨和原料之间被解耦,往后拓展不同口味披萨,建立不同的披萨工厂也很方便 Pizza类被复用
*/
// 准备
public abstract void prepare();
// 烘烤
public void bake() {
System.out.println("\n" + "披萨正在烘烤,需烘烤25分钟。。。");
}
// 切片
public void cut() {
System.out.println("制作完成,给披萨切片。。。");
}
// 装盒
public void box() {
System.out.println("给披萨打包装盒。。。");
}
// 输出客人点的披萨信息
@Override
public String toString() {
StringBuffer display = new StringBuffer();
display.append(name + "\n");
if (dough != null) {
display.append(dough + "\t");
}
if (sauce != null) {
display.append(sauce + "\n");
}
if (cheese != null) {
display.append(cheese + "\t");
}
if (veggies != null) {
for (int i = 0; i < veggies.length; i++) {
display.append(veggies[i] + ", ");
}
}
if (pepperoni != null) {
display.append(pepperoni + "\n");
}
if (clams != null) {
display.append(clams + "\t");
}
return display.toString();
}
}
下面是具体Pizza实现
package Pizza;
import Factory_Interface.PizzaIngredientFactory;
//芝士披萨
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
// 要制作披萨,要先从对于工厂获取原料,所以构造器初始化数据
public CheesePizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
// 这里就不在以前Pizza类直接写死,而是更加灵活使用
// prepare()方法一步步创建芝士披萨,需要的原料全部从“工厂”中获取
@Override
public void prepare() {
System.out.println("准备 " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
}
}
package Pizza;
import Factory_Interface.PizzaIngredientFactory;
//海蛎披萨
public class ClamPizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public ClamPizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
public void prepare() {
System.out.println("准备 " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
clams = ingredientFactory.createClam();
}
}
package Pizza;
import Factory_Interface.PizzaIngredientFactory;
//香肠披萨
public class PepperoniPizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public PepperoniPizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
public void prepare() {
System.out.println("准备 " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
veggies = ingredientFactory.createVeggies();
pepperoni = ingredientFactory.createPepperoni();
}
}
package Pizza;
import Factory_Interface.PizzaIngredientFactory;
//素食披萨
public class VeggiePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public VeggiePizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
public void prepare() {
System.out.println("准备 " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
veggies = ingredientFactory.createVeggies();
}
}
下面是PizzaStore接口
package Store;
import Pizza.Pizza;
/**
* PizzaStore相当于客户, 往后会有很多类似此类进行拓展
*
* 在这里orderPizza()方法对pizz对象做了很多事(createPizza,prepare,bake,cut,box)
* 但是由于Pizza对象是抽象的(接口),
* orderPizza()并不知道具体哪些实际类参与进来了(相当于实现Pizza接口的实现类有哪些orderPizza()方法并不知道)
* 换句话说,这就是解耦,减少相互依赖
*
* 同理createPizza()方法中也不知道创建的具体是什么披萨 只知道披萨可以被准备,被烘烤,被切片,被装盒行为而已
*
*/
// 披萨店
public abstract class PizzaStore {
// 点单
public Pizza orderPizza(String type) {
// createPizza()方法从工厂对象中取出,
// 并且方法也不再这里实现,定义为抽象类,谁需要在拿走方法去具体实现
Pizza pizza = createPizza(type);
System.out.print("----制作一个" + pizza);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
// 把工厂对象移到这个方法里,并封装
// 现在工厂方法为抽象的,子类必须实现这个抽象方法
public abstract Pizza createPizza(String item);
}
下面是两个加盟店的具体实现
package Store;
import Factory_Implements.NYPizzaIngredientFactory;
import Factory_Interface.PizzaIngredientFactory;
import Pizza.CheesePizza;
import Pizza.ClamPizza;
import Pizza.PepperoniPizza;
import Pizza.Pizza;
import Pizza.VeggiePizza;
/**
* 此时NYStylePizzaStore所封装的知识是关于如何制作纽约风格的比萨
*
* @author Joy
*
*/
// 纽约风格披萨店(分店)
public class NYStylePizzaStore extends PizzaStore {
/**
* 相比之前写的这里添加了工厂类
*/
// 根据披萨的不同,创建不同披萨(具体实现类)
@Override
public Pizza createPizza(String item) {
Pizza pizza = null;
// 这里多态创建纽约原料工厂,该工厂负责纽约风味的披萨
PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
if (item.equals("芝士")) {
// 实例化具体披萨类型,并传进该比萨所需要的工厂,以便取得原料
pizza = new CheesePizza(ingredientFactory);
pizza.setName("纽约芝士披萨");
} else if (item.equals("素食")) {
pizza = new VeggiePizza(ingredientFactory);
pizza.setName("纽约素食披萨");
} else if (item.equals("海蛎")) {
pizza = new ClamPizza(ingredientFactory);
pizza.setName("纽约海蛎披萨");
} else if (item.equals("香肠")) {
pizza = new PepperoniPizza(ingredientFactory);
pizza.setName("纽约香肠披萨");
}
return pizza;
}
}
package Store;
import Factory_Implements.ChicagoPizzaIngredientFactory;
import Factory_Interface.PizzaIngredientFactory;
import Pizza.CheesePizza;
import Pizza.ClamPizza;
import Pizza.PepperoniPizza;
import Pizza.Pizza;
import Pizza.VeggiePizza;
/**
* 此时ChicagoStylePizzaStore所封装的知识是关于如何制作芝加哥风格的比萨
*
* @author Joy
*
*/
// 芝加哥披萨店(分店)
public class ChicagoStylePizzaStore extends PizzaStore {
Pizza pizza = null;
// 这里多态创建纽约原料工厂,该工厂负责纽约风味的披萨
PizzaIngredientFactory ingredientFactory = new ChicagoPizzaIngredientFactory();
@Override
public Pizza createPizza(String item) {
if (item.equals("芝士")) {
pizza = new CheesePizza(ingredientFactory);
pizza.setName("芝加哥芝士披萨");
} else if (item.equals("素食")) {
pizza = new VeggiePizza(ingredientFactory);
pizza.setName("芝加哥素食披萨");
} else if (item.equals("海鲜")) {
pizza = new ClamPizza(ingredientFactory);
pizza.setName("芝加哥海蛎披萨");
} else if (item.equals("香肠")) {
pizza = new PepperoniPizza(ingredientFactory);
pizza.setName("芝加哥香肠披萨");
}
return pizza;
}
}
测试类
package TestMain;
import Pizza.Pizza;
import Store.ChicagoStylePizzaStore;
import Store.NYStylePizzaStore;
import Store.PizzaStore;
public class TestMain {
public static void main(String[] args) {
PizzaStore nyStore = new NYStylePizzaStore();
PizzaStore chicagoStore = new ChicagoStylePizzaStore();
System.out.println("====================================");
Pizza pizza3 = nyStore.orderPizza("香肠");
System.out.println("Joy点了一个" + pizza3);
System.out.println("====================================");
Pizza pizza4=chicagoStore.orderPizza("海鲜");
System.out.println("Joy点了一个" + pizza4);
}
}
效果图:
工厂模式给我最大感受就是对编写好的代码的理解程度加深,想法也更加考虑拓展了,当然还需反复多练习,模拟代码已经全部放出,注释也写的比较清楚,若有不理解欢迎留言
感谢你看到这里,至此工厂模式内容结束,本人文笔随便,若有不足或错误之处望给予指点,90度弯腰~~~很快我会发布下一个设计模式内容,生命不息,编程不止!
参考书籍:《Head First 设计模式》
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。