摘要:在之前内容里,介绍了工厂模式中的简单工厂和工厂方法内容,这我们继续工厂模式的学习,今天学习抽象工厂模式。这样的工厂形式就是抽象工厂模式抽象工厂模式提供一个接口,用于创建相关或依赖对象,而不需要明确指定具体类。
在之前内容里,介绍了工厂模式中的简单工厂和工厂方法内容,这我们继续工厂模式的学习,今天学习抽象工厂模式。
当直接实例化一个对象时,就是在依赖它的具体类。看一张对象依赖图
很明显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(" " + "披萨正在烘烤,需烘烤25分钟。。。"); } // 切片 public void cut() { System.out.println("制作完成,给披萨切片。。。"); } // 装盒 public void box() { System.out.println("给披萨打包装盒。。。"); } // 输出客人点的披萨信息 @Override public String toString() { StringBuffer display = new StringBuffer(); display.append(name + " "); if (dough != null) { display.append(dough + " "); } if (sauce != null) { display.append(sauce + " "); } if (cheese != null) { display.append(cheese + " "); } if (veggies != null) { for (int i = 0; i < veggies.length; i++) { display.append(veggies[i] + ", "); } } if (pepperoni != null) { display.append(pepperoni + " "); } if (clams != null) { display.append(clams + " "); } 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 设计模式》
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/70195.html
摘要:对象字面量创建对象张三学生这种方式的好处显而易见,就是解决了之前的缺点。构造函数模式张三学生李四学生与之前工厂模式的方法对比变量名首字母大写了在函数内没有显式的创建及返回对象而使用了创建时使用了关键字。 面向对象是JS的重点与难点,但也是走向掌握JS的必经之路,有很多的文章或书籍中都对其进行了详细的描述,本没有必要再写这些,但是对于学习来说,讲给别人听对自己来说是一种更好的受益方式。我...
摘要:对象字面量创建对象张三学生这种方式的好处显而易见,就是解决了之前的缺点。构造函数模式张三学生李四学生与之前工厂模式的方法对比变量名首字母大写了在函数内没有显式的创建及返回对象而使用了创建时使用了关键字。 面向对象是JS的重点与难点,但也是走向掌握JS的必经之路,有很多的文章或书籍中都对其进行了详细的描述,本没有必要再写这些,但是对于学习来说,讲给别人听对自己来说是一种更好的受益方式。我...
摘要:设计模式的定义在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案。从前由于使用的局限性,和做的应用相对简单,不被重视,就更谈不上设计模式的问题。 ‘从大处着眼,从小处着手’,以前对这句话一知半解,自从踏出校门走入社会,开始工作以来,有了越来越深的理解,偶有发现这句话用在程序开发中也有用,所以,近段时间开始尝试着分析jQuery源码,分析angularjs源码,学习设计模式。 设...
摘要:结构型模式适配器模式桥接模式装饰模式组合模式外观模式享元模式代理模式。行为型模式模版方法模式命令模式迭代器模式观察者模式中介者模式备忘录模式解释器模式模式状态模式策略模式职责链模式责任链模式访问者模式。 主要版本 更新时间 备注 v1.0 2015-08-01 首次发布 v1.1 2018-03-12 增加新技术知识、完善知识体系 v2.0 2019-02-19 结构...
阅读 3005·2021-10-12 10:20
阅读 2770·2021-09-27 13:56
阅读 753·2021-09-27 13:36
阅读 1387·2021-09-26 09:46
阅读 2383·2019-08-30 14:02
阅读 2658·2019-08-28 18:14
阅读 1224·2019-08-26 10:32
阅读 1652·2019-08-23 18:25