04工厂模式
制造对象,不仅只有使用new操作符。实例化不应该总是公开进行,因为经常会导致耦合问题。
01例子
假设你有一个披萨店,身为披萨店的主人,代码可能是这样:
Pizza orderPizza(){ //为了让系统有弹性,我们很希望这是一个抽象类或接口。但如果这样,这些类或接口就无法直接实例化。 Pizza pizza =new Pizza(); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; }
需要更多披萨类型时:
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; }
缺点:如果要增加或者删除某个type,需要对这份代码进行修改。
关于哪个具体类被实例化才是搞乱orderPizza()方法的祸首:
它无法让orderPizza()对修改关闭;但是,现在我们已经知道哪些会改变,哪些不会改变,该是封装的时候了。
02简单工厂
建立简单工厂
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; } }
重写PizzaStore类
依赖工厂来创建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; } }
类图
简单工厂不是设计模式,是一种编程习惯!
03工厂方法
加盟披萨店
如果利用SimplePizzaFactory,写出三种不同的工厂,分别是
NYPizzaFactory,ChicagoPizzaFactory,CaliforniaPizzaFactory,各地加盟店都有适合的工厂可以使用,这是一种做法。
NYPizzaFactory nyFactory=new NYPizzaFactory();//创建纽约风味pizza的工厂 PizzaStore nyStore=new PizzaStore(nyFactory);//创建pizzastore nyFactory.orderPizza("Veggie");//制作pizza,得到纽约风味的;
缺点:不能质量控制;目的:只想创建pizza不同,其他操作相同;
框架
02简单工厂中,在SimplePizzaFactory前,把制作pizza绑到了store,失去了弹性。【需要】:创建框架,把加盟店和pizza创建绑定在一起,但允许保持弹性
有个做法可让披萨制作轰动局限于PizzaStore类,而同时又能让这些加盟店依然可以自由地制作该区域的风味。
把createPizza()方法放回到PizzaStore中,不过要把它设置成“抽象方法”,然后为每个区域风味创建一个PizzaStore的子类。
public abstract class PizzaStore(){ public Pizza orderPizza(String type){ Pizza pizza; pizza=createPizza(type);//从工厂对象移回pizzastore;【不是factory.createPizza()】 pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } public abstract Pizza createPizza(String type);//把工厂对象移到这个方法中 //作为抽象方法,为每个区域风味创建一个pizzastore子类 }
子类:
orderPizza()方法在抽象的pizzastore中定义,所以,该方法不知道哪个子类实际运行代码并制作pizza;【即不知道哪个实际的具体类【解耦了】】
实现
加盟店只需要继承pizzastore,并提供自己的pizza风味的creatPizza()方法。
纽约风味店:
class NYPizzaStore extends PizzaStore{ Pizza createPizza(String type){ if(type.equals("cheese")){ pizza=new NYStyleCheesePizza();//创建具体类 }else if(type.equals("pepperoni")){ pizza=new NYStylePepperoniPizza(); }else if(type.equals("clam")){ pizza=new NYStyleClamPizza(); }else if(type.equals("veggie")){ pizza=new NYStyleVeggiePizza(); } } }
工厂方法(以上框架)
对pizzastore类做一些转换,从由一个对象处理具体类的实例化,到由一个子类集合来承担责任。
public abstract class PizzaStore(){ public Pizza orderPizza(String type){ Pizza pizza; pizza=createPizza(type);//从工厂对象移回pizzastore;【不是factory.createPizza()】 //pizzastore的子类在creatpizza()中处理对象的实例化 pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } public abstract Pizza createPizza(String type);//把工厂对象移到这个方法中 //作为抽象方法,为每个区域风味创建一个pizzastore子类 //实例化pizza的责任被转到了方法中(该方法扮演工厂的角色) }
Pizza类
public abstract class Pizza{ String name; String dough; String sauce; ArrayList toppings =new ArrayList(); public void prepare(){ System.out.println("Preparing"+name); System.out.println("Tossing dough"); System.out.println("Adding sauce"); System.out.println("Adding toppings:"); for(int i=0;i<toppings.size();i++){ System.out.println(""+toppings.get(i)); } } 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; } }
Pizza的一些具体子类
public class NYStyleCheesePizza extends Pizza{ public NYStyleCheesePizza(){ name="NY Style Sauce and Cheese Pizza"; dough="Thin Crust Dough"; sauce="Marinara Sauce"; toppings.add("Grated Reggiano Cheese"); } }
测试:
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") } }
工厂方法模式
工厂方法模式:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
工厂方法模式能够封装具体类型的实例化,抽象的Creator提供了一个创建对象的方法的接口,在抽象的Creator中,任何其他实现的方法,都可能使用到这个工厂方法所制造出来的产品,但只有子类真正实现这个工厂方法并创建产品。
例子类图:
工厂方法是封装的关键
平行视角:平行的类层级
04依赖倒置原则
对象依赖
当你直接实例化一个对象时,就是在依赖它的具体类,返回前页看看这个依赖性很高的比萨店例子,它由披萨店类来创建所有的披萨对象,而不是委托给工厂。
依赖倒置原则:要依赖抽象,不要依赖具体类。
依赖倒置原则更强调抽象。说明高层组件不能依赖底层组件,而且,不管高层或底层组件,都应该依赖抽象。
PizzaStore是“高层组件”,而披萨实现是“底层组件”,很清楚地,PizzaStore依赖这些具体披萨类
应用原则
此时高层组件PizzaStore和底层组件(就是这些比萨)都依赖了Pizza抽象。
05抽象工厂
需求
建造一家成产原料的工厂,并将原料运送到各家加盟店【有些加盟店,使用低价原料来增加利润,你必须采取一些手段,以免回调你的披萨店平牌】
建造原料厂
public interface PizzaIngredientFactory{ public Dough createDough(); public Sauce createSauce(); public Cheese createCheese(); public Veggies[] createVeggies(); public Pepperoni createPepperoni(); public Clams createClams(); }
要做的事情是:
1.为每个区域建造一个工厂,需要创建一个继承自PizzaIngredientFactory的子类来实现每一个创建方法。
2.实现一组原料类供工厂使用,例如ReggianoCheese,RedPeppers,ThickCrustDough.这些类可以在何时的区域间共享。
3.然后你仍然需要将这一切组织起来,将新的原料工厂整合进旧的PizzaStore
创建原料工厂子类---纽约
public class NYPizzaIngredientFactory implements PizzaIngredientFactory{ @Override public Dough createDough() { return new ThinCrusDough(); } @Override public Sauce createSauce() { return new MarinaraSauce(); } @Override public Cheese createCheese() { return ReggianoCheese(); } @Override public Veggies[] createVeggies() { Veggies veggies[] = {new Garlic(),new Onion(),new Mushroom(),new RedPepper()}; return new Veggies; } @Override public Pepperoni createPepperoni() { return new SlicedPepperoni(); } @Override public Clams createClam() { return FreshClams(); } }
重做Pizza类
public abstract class Pizza{ //每个pizza持有一组原料 String name; Dough dough; Sauce sauce; Veggies veggies[]; Cheese cheese; Pepperoni pepperoni; Clams clams; public abstract void prepare();//该方法收集来自工厂的原料 //除了prepare()方法,其他方法保持不变 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 void setName(String name){ this.name=name; } public String getName(){ return name; } public void toString2(){ //这里打印披萨的代码 } }
重做Pizza类的子类
class CheesePizza extends Pizza{ PizzaIngredientFactory ingredientFactory; public CheesePizza(PizzaIngredientFactory ingredientFactory){ this.ingredientFactory=ingredientFactory; } @Override public void prepare() { System.out.println("Preparing"+name); //每次需要原理的时候,请求工厂生产 dough=ingredientFactory.createDough(); sauce=ingredientFactory.createSauce(); cheese=ingredientFactory.createCheese(); } }
PizzaStore类的子类
public class NYPizzaStore extends PizzaStore{ protected Pizza createPizza(String item){ Pizza pizza=null; PizzaIngredientFactory ingredientFactory=new NYPizzaIngredientFactory(); if(item.equals("cheese")){ pizza=new CheesePizza(ingredientFactory); pizza.setName("New York Style Cheese Pizza"); }else if(item.equals("veggie")){ //…… } return pizza; } }
抽象工厂
抽象工厂模式:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
类图:
pizzastore类图
工厂方法和抽象工厂比较
工厂方法
用工厂方法将客户代码从需要实例化的具体类解耦,或者如果事先不知道会需要哪些具体类时,用工厂方法只需要子类化并实现工厂方法!
抽象工厂
任何时候需要创建产品家族,以及想确保客户创建相互关联的产品时,使用抽象工厂。
总结
OO原则:
封装变化
组合优于集承
针对接口编程、而不是针对实现
尽力达到交互对象之间的松耦合设计
类应该对扩展开放,对修改关闭
依赖于抽象,不要依赖于具体类
【我们有了一条新的原则,指导我们尽可能地让事情保持抽象。】
OO模式:
抽象工厂,提供一个接口,用于创建相关或依赖对象的家族,而不必指定它们的具体类
工厂方法,定义一个创建对象的接口,但让子类决定哪个类要实例化。工厂方法让一个类延迟实例化到子类。
要点
1.所有工厂都封装对象的创建。
2.简单工厂虽然不是真正体设计模式,但依然可以为一个简单的方法,将客户从具体类解耦
3.工厂方法靠继承:对象创建被委托给子类,子类实现工厂方法来创建对象
4.抽象工厂靠对象组合:对象创建在工厂接口暴露的方法中实现。
5.所有工厂模式都通过减少应用对具体类的依赖,促进了松耦合。
6.工厂方法的意图,是允许个类延迟实例化到其子类。
7.抽象工厂的意图,是创建相关对象家族,不必依赖于其具体类。
8.依赖倒置原则指导我们避免依赖具体类型,尽量依赖抽象。
9.工厂是强有力的技巧,让我们针对抽象编码,而不是针对具体类。
本文作者:Lee_ing
本文链接:https://www.cnblogs.com/yunshalee/p/17326694.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步