结合简单示例和UML图,讲解工厂模式简单原理。html
1、引子java
话说十年前,有一个爆发户,他家有三辆汽车(Benz(奔驰)、Bmw(宝马)、Audi(奥迪)),还雇了司机为他开车。不过,爆发户坐车时老是这样:上Benz车后跟司机说“开奔驰车!”,坐上Bmw后他说“开宝马车!”,坐上 Audi后他说“开奥迪车!”。
你必定说:这人有病!直接说开车不就好了?!而当把这个爆发户的行为放到咱们程序语言中来,咱们发现C语言一直是经过这种方式来坐车的!
幸运的是这种有病的现象在OO语言中能够避免了。下面以Java语言为基础来引入咱们本文的主题:工厂模式!算法
2、简介数组
工厂模式主要是为建立对象提供了接口。工厂模式按照《Java与模式》中的提法分为三类:
1. 简单工厂模式(Simple Factory)
2. 工厂方法模式(Factory Method)
3. 抽象工厂模式(Abstract Factory)
这三种模式从上到下逐步抽象,而且更具通常性。还有一种分类法,就是将简单工厂模式看为工厂方法模式的一种特例,两个归为一类。二者皆可,这本为使用《Java与模式》的分类方法。
在什么样的状况下咱们应该记得使用工厂模式呢?大致有两点:
1.在编码时不能预见须要建立哪一种类的实例。
2.系统不该依赖于产品类实例如何被建立、组合和表达的细节
工厂模式能给咱们的OOD、OOP带来哪些好处呢??this
3、简单工厂模式编码
这个模式自己很简单并且使用在业务较简单的状况下。通常用于小项目或者具体产品不多扩展的状况(这样工厂类才不用常常更改)。
它由三种角色组成:
工厂类角色:这是本模式的核心,含有必定的商业逻辑和判断逻辑,根据逻辑不一样,产生具体的工厂产品。如例子中的Driver类。
抽象产品角色:它通常是具体产品继承的父类或者实现的接口。由接口或者抽象类来实现。如例中的Car接口。
具体产品角色:工厂类所建立的对象就是此角色的实例。在java中由一个具体类实现,如例子中的Benz、Bmw类。spa
来用类图来清晰的表示下的它们之间的关系:设计
下面就来给那个暴发户治病:在使用了简单工厂模式后,如今暴发户只须要坐在车里对司机说句:“开车”就能够了。来看看怎么用代码实现的:(为方便起见,全部的类放在一个文件中,故有一个类被声明为public)code
//抽象产品 abstract class Car{ private String name; public abstract void drive(); public String getName() { return name; } public void setName(String name) { this.name = name; } } //具体产品 class Benz extends Car{ public void drive(){ System.out.println(this.getName()+"----go-----------------------"); } } class Bmw extends Car{ public void drive(){ System.out.println(this.getName()+"----go-----------------------"); } } //简单工厂 class Driver{ public static Car createCar(String car){ Car c = null; if("Benz".equalsIgnoreCase(car)) c = new Benz(); else if("Bmw".equalsIgnoreCase(car)) c = new Bmw(); return c; } } //老板 public class BossSimplyFactory { public static void main(String[] args) throws IOException { //老板告诉司机我今天坐奔驰 Car car = Driver.createCar("benz"); car.setName("benz"); //司机开着奔驰出发 car.drive(); } <span style="font-family: courier new,courier;">}</span>
若是老板要坐奥迪,同理。htm
这即是简单工厂模式了。那么它带了了什么好处呢?
首先,符合现实中的状况;并且客户端免除了直接建立产品对象的责任,而仅仅负责“消费”产品(正如暴发户所为)。
下面咱们从开闭原则上来分析下简单工厂模式。当暴发户增长了一辆车的时候,只要符合抽象产品制定的合同,那么只要通知工厂类知道就能够被客户使用了。(即建立一个新的车类,继承抽象产品Car)那么 对于产品部分来讲,它是符合开闭原则的——对扩展开放、对修改关闭;可是工厂类不太理想,由于每增长一辆车,都要在工厂类中增长相应的商业逻辑和判 断逻辑,这显天然是违背开闭原则的。
而在实际应用中,极可能产品是一个多层次的树状结构。因为简单工厂模式中只有一个工厂类来对应这些产品,因此这可能会把咱们的上帝类坏了。
正如我前面提到的简单工厂模式适用于业务简单的状况下或者具体产品不多增长的状况。而对于复杂的业务环境可能不太适应了。这就应该由工厂方法模式来出场了!!
4、工厂方法模式
抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。
具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以建立对应的具体产品的对象。在java中它由具体的类来实现。
抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中通常有抽象类或者接口来实现。
具体产品角色:具体工厂角色所建立的对象就是此角色的实例。在java中由具体的类来实现。
来用类图来清晰的表示下的它们之间的关系:
话说暴发户生意越作越大,本身的爱车也愈来愈多。这可苦了那位司机师傅了,什么车它都要记得,维护,都要通过他来使用!因而暴发户同情他说:我给你分配几我的手,你只管管好他们就好了!因而工厂方法模式的管理出现了。代码以下:
//抽象产品 abstract class Car{ private String name; public abstract void drive(); public String getName() { return name; } public void setName(String name) { this.name = name; } } //具体产品 class Benz extends Car{ public void drive(){ System.out.println(this.getName()+"----go-----------------------"); } } class Bmw extends Car{ public void drive(){ System.out.println(this.getName()+"----go-----------------------"); } } //抽象工厂 abstract class Driver{ public abstract Car createCar(String car) throws Exception; } //具体工厂(每一个具体工厂负责一个具体产品) class BenzDriver extends Driver{ public Car createCar(String car) throws Exception { return new Benz(); } } class BmwDriver extends Driver{ public Car createCar(String car) throws Exception { return new Bmw(); } } //老板 public class Boss{ public static void main(String[] args) throws Exception { Driver d = new BenzDriver(); Car c = d.createCar("benz"); c.setName("benz"); c.drive(); } }
使用开闭原则来分析下工厂方法模式。当有新的产品(即暴发户的汽车)产生时,只要按照抽象产品角色、抽象工厂角色提供的合同来生成,那么就能够被客户使用,而没必要去修改任何已有的代码。(即当有新产品时,只要建立并基础抽象产品;新建具体工厂继承抽象工厂;而不用修改任何一个类)工厂方法模式是彻底符合开闭原则的!
使用工厂方法模式足以应付咱们可能遇到的大部分业务需求。可是当产品种类很是多时,就会出现大量的与之对应的工厂类,这不该该是咱们所但愿的。因此我建议在这种状况下使用简单工厂模式与工厂方法模式相结合的方式来减小工厂类:即对于产品树上相似的种类(通常是树的叶子中互为兄弟的)使用简单工厂模式来实现。
固然特殊的状况,就要特殊对待了:对于系统中存在不一样的产品树,并且产品树上存在产品族(下一节将解释这个名词)。那么这种状况下就可能可使用抽象工厂模式了。
5、小结
让咱们来看看简单工厂模式、工厂方法模式给咱们的启迪:
若是不使用工厂模式来实现咱们的例子,也许代码会减小不少——只须要实现已有的车,不使用多态。可是在可维护性上,可扩展性上是很是差的(你能够想象一下添加一辆车后要牵动的类)。所以为了提升扩展性和维护性,多写些代码是值得的。
6、抽象工厂模式
先来认识下什么是产品族: 位于不一样产品等级结构中,功能相关联的产品组成的家族。
图中的BmwCar和BenzCar就是两个产品树(产品层次结构);而如图所示的BenzSportsCar和BmwSportsCar就是一个产品族。他们均可以放到跑车家族中,所以功能有所关联。同理BmwBussinessCar和BenzBusinessCar也是一个产品族。
能够这么说,它和工厂方法模式的区别就在于须要建立对象的复杂程度上。并且抽象工厂模式是三个里面最为抽象、最具通常性的。抽象工厂模式的用意为:给客户端提供一个接口,能够建立多个产品族中的产品对象。
并且使用抽象工厂模式还要知足一下条件:
1.系统中有多个产品族,而系统一次只可能消费其中一族产品
2.同属于同一个产品族的产品以其使用。
来看看抽象工厂模式的各个角色(和工厂方法的一模一样):
抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。
具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以建立对应的具体产品的对象。在java中它由具体的类来实现。
抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中通常有抽象类或者接口来实现。
具体产品角色:具体工厂角色所建立的对象就是此角色的实例。在java中由具体的类来实现。
//抽象产品(Bmw和Audi同理) abstract class BenzCar{ private String name; public abstract void drive(); public String getName() { return name; } public void setName(String name) { this.name = name; } } //具体产品(Bmw和Audi同理) class BenzSportCar extends BenzCar{ public void drive(){ System.out.println(this.getName()+"----BenzSportCar-----------------------"); } } class BenzBusinessCar extends BenzCar{ public void drive(){ System.out.println(this.getName()+"----BenzBusinessCar-----------------------"); } } abstract class BmwCar{ private String name; public abstract void drive(); public String getName() { return name; } public void setName(String name) { this.name = name; } } class BmwSportCar extends BmwCar{ public void drive(){ System.out.println(this.getName()+"----BmwSportCar-----------------------"); } } class BmwBusinessCar extends BmwCar{ public void drive(){ System.out.println(this.getName()+"----BmwBusinessCar-----------------------"); } } abstract class AudiCar{ private String name; public abstract void drive(); public String getName() { return name; } public void setName(String name) { this.name = name; } } class AudiSportCar extends AudiCar{ public void drive(){ System.out.println(this.getName()+"----AudiSportCar-----------------------"); } } class AudiBusinessCar extends AudiCar{ public void drive(){ System.out.println(this.getName()+"----AudiBusinessCar-----------------------"); } } //抽象工厂 abstract class Driver3{ public abstract BenzCar createBenzCar(String car) throws Exception; public abstract BmwCar createBmwCar(String car) throws Exception; public abstract AudiCar createAudiCar(String car) throws Exception; } //具体工厂 class SportDriver extends Driver3{ public BenzCar createBenzCar(String car) throws Exception { return new BenzSportCar(); } public BmwCar createBmwCar(String car) throws Exception { return new BmwSportCar(); } public AudiCar createAudiCar(String car) throws Exception { return new AudiSportCar(); } } class BusinessDriver extends Driver3{ public BenzCar createBenzCar(String car) throws Exception { return new BenzBusinessCar(); } public BmwCar createBmwCar(String car) throws Exception { return new BmwBusinessCar(); } public AudiCar createAudiCar(String car) throws Exception { return new AudiBusinessCar(); } } //老板 public class BossAbstractFactory { public static void main(String[] args) throws Exception { Driver3 d = new BusinessDriver(); AudiCar car = d.createAudiCar(""); car.drive(); } }
其中:BenzSportCar和BenzBusinessCar属于产品树;同理BmwSportCar和BmwBusinessCar。而BenzSportCar和BmwSportCar和AudiSportCar属于产品族。
因此抽象工厂模式通常用于具备产品树和产品族的场景下。
抽象工厂模式的缺点:若是须要增长新的产品树,那么就要新增三个产品类,好比VolvoCar,VolvoSportCar,VolvoSportCar,而且要修改三个工厂类。这样大批量的改动是很丑陋的作法。
因此能够用简单工厂配合反射来改进抽象工厂:
UML图略。
abstract class BenzCar{ private String name; public abstract void drive(); public String getName() { return name; } public void setName(String name) { this.name = name; } } class BenzSportCar extends BenzCar{ public void drive(){ System.out.println(this.getName()+"----BenzSportCar-----------------------"); } } class BenzBusinessCar extends BenzCar{ public void drive(){ System.out.println(this.getName()+"----BenzBusinessCar-----------------------"); } } abstract class BmwCar{ private String name; public abstract void drive(); public String getName() { return name; } public void setName(String name) { this.name = name; } } class BmwSportCar extends BmwCar{ public void drive(){ System.out.println(this.getName()+"----BmwSportCar-----------------------"); } } class BmwBusinessCar extends BmwCar{ public void drive(){ System.out.println(this.getName()+"----BmwBusinessCar-----------------------"); } } abstract class AudiCar{ private String name; public abstract void drive(); public String getName() { return name; } public void setName(String name) { this.name = name; } } class AudiSportCar extends AudiCar{ public void drive(){ System.out.println(this.getName()+"----AudiSportCar-----------------------"); } } class AudiBusinessCar extends AudiCar{ public void drive(){ System.out.println(this.getName()+"----AudiBusinessCar-----------------------"); } } /** * 简单工厂经过反射改进抽象工厂及其子工厂 * @author Administrator * */ class Driver3{ public static BenzCar createBenzCar(String car) throws Exception { return (BenzCar) Class.forName(car).newInstance(); } public static BmwCar createBmwCar(String car) throws Exception { return (BmwCar) Class.forName(car).newInstance(); } public static AudiCar createAudiCar(String car) throws Exception { return (AudiCar) Class.forName(car).newInstance(); } } //客户端 public class SimpleAndAbstractFactory { public static void main(String[] args) throws Exception { AudiCar car = Driver3.createAudiCar("com.java.pattendesign.factory.AudiSportCar"); car.drive(); } }
策略模式
从策略一词来看,策略模式是种倾向于行为的模式.有点相似找仗时的作战方案,通常司令员在作战前都会根据实际状况作出几套不一样的方案,若是当时状况有变,就会根据相应的条件来断定用哪一套方案来替换原定方案。但不管如何替换,替换多少次,仗仍是要打的。
举例:导出成EXCEL,WORD,PDF文件的功能,这三类导出虽然具体操做略有不一样,可是大部分都相同。
策略模式与工厂模式从uml图上来讲,基本一致。只是强调的封装不一样。咱们以工厂模式和策略模式的比较来说解策略模式。
工厂模式咱们能够作以下理解:假设有Audi的公司生产汽车,它掌握一项核心的技术就是生产汽车,另外一方面,它生产的汽车是有不一样型号的,而且在不一样的生产线上进行组装。当客户经过销售部门进行预约后,Audi公司将在指定的生产线上为客户生产出它所须要的汽车。
策略(Strategy)模式在结构上与工厂模式相似,惟一的区别是工厂模式实例化一个产品的操做是在服务端来作的,换句话说客户端传达给服务端的只是某种标识,服务端根据该标识实例化一个对象。而策略模式的客户端传达给服务端的是一个实例,服务端只是将该实例拿过去在服务端的环境里执行该实例的方法。这就比如一个对汽车不甚了解的人去买车,他在那一比划,说要什么什么样的,销售部门根据他的这个“比划”来造成一份订单,这就是工厂模式下的工做方式。而策略模式下那个顾客就是个行家,他本身给出了订单的详细信息,销售部门只是转了一下手就交给生产部门去作了。经过两相对比,咱们不难发现,采用工厂模式必须提供足够灵活的销售部门,若是用户有了新的需求,销售部门必须立刻意识到这样才能够作出合适的订单。因此倘一款新车出来了,生产部门和销售部门都须要更新,对顾客来讲也须要更新对新车的描述因此须要改动的地方有三处。而策略模式中的销售部门工做比较固定,它只负责接受订单并执行特定的几个操做。当一款新车出来时,只须要对服务端的生产部门和客户端的代码进行更新,而不须要更新销售部门的代码。
技术支持: 简单工厂和策略的基础都是由于面向对象的封装与多态。他们实现的思想都是先设定一个抽象的模型并从该模型派生出符合不一样客户需求的各类方法,并加以封装。
工厂模式和策略模式的区别在于实例化一个对象的位置不一样,对工厂模式而言,实例化对象是放在服务端的,即放在了工厂类里面;
而策略模式实例化对象的操做在客户端,服务端的“销售部门”只负责传递该对象,并在服务端的环境里执行特定的操做。。。
工厂模式要求服务端的销售部门足够灵敏,而策略模式因为对策略进行了封装,因此他的销售部门比较傻,须要客户提供足够能区分使用哪一种策略的参数,而这最好的就是该策略的实例了。
//抽象产品 abstract class AudiCar{ private String name; public abstract void makeCar(); public String getName() { return name; } public void setName(String name) { this.name = name; } } //具体产品 class AudiA6 extends AudiCar{ public void makeCar(){ System.out.println(this.getName()+"----go-----------------------"); } } class AudiA4 extends AudiCar{ public void makeCar(){ System.out.println(this.getName()+"----go-----------------------"); } } //销售部门----服务端 class CarContext { AudiCar audiCar = null; public CarContext(AudiCar audiCar) { this.audiCar = audiCar; } public void orderCar(){ this.audiCar.makeCar(); } } //客户----客户端(这个客户是内行,什么都懂,他说我要A6,销售部门马上给他a6,因此销售部门不用很懂) public class SimplyFactoryAndStrategy2 { public static void main(String[] args) throws IOException { //客户说我要什么什么样子的车子,销售人员才知道他要什么样子的车子 AudiCar car = new AudiA6(); car.setName("a6"); CarContext context = new CarContext(car); context.orderCar(); } } //工厂模式---与上面的策略模式比较 //抽象产品 abstract class AudiCar{ private String name; public abstract void makeCar(); public String getName() { return name; } public void setName(String name) { this.name = name; } } //具体产品 class AudiA6 extends AudiCar{ public void makeCar(){ System.out.println(this.getName()+"----go-----------------------"); } } class AudiA4 extends AudiCar{ public void makeCar(){ System.out.println(this.getName()+"----go-----------------------"); } } //简单工厂---销售部门----服务端 class CarFactroy{ public static AudiCar createCar(String car){ AudiCar c = null; if("A6".equalsIgnoreCase(car)) c = new AudiA6(); else if("A4".equalsIgnoreCase(car)) c = new AudiA4(); return c; } } //客户----客户端(这个客户是外行,什么都不懂,只要随便描述下车,销售部门才能知道他要那款车,因此销售部门比较牛) public class SimplyFactoryAndStrategy { public static void main(String[] args) throws IOException { System.out.print("请输入您要坐的车:(A六、A4)"); String carName = new BufferedReader(new InputStreamReader(System.in)).readLine(); //客户说我要什么什么样子的车子,销售人员才知道他要什么样子的车子 AudiCar car = CarFactroy.createCar(carName); car.setName(carName); car.makeCar(); } }
策略模式的优缺点
策略模式的主要优势有:
策略模式的缺点主要有两个:
适用场景
作面向对象设计的,对策略模式必定很熟悉,由于它实质上就是面向对象中的继承和多态,在看完策略模式的通用代码后,我想,即便以前历来没有据说过策略模式,在开发过程当中也必定使用过它吧?至少在在如下两种状况下,你们能够考虑使用策略模式,
策略模式是一种简单经常使用的模式,咱们在进行开发的时候,会常常有意无心地使用它,通常来讲,策略模式不会单独使用,跟模版方法模式、工厂模式等混合使用的状况比较多。
大粒度的 if --else if...可使用 工厂+策略模式搞定。