简单工厂模式并不属于 GoF 23 个经典设计模式,但一般将它做为学习其余工厂模式的基础,它的设计思想很简单,其基本流程以下:html
首先将须要建立的各类不一样对象(例如各类不一样的 Chart 对象)的相关代码封装到不一样的类中,这些类称为具体产品类,而将它们公共的代码进行抽象和提取后封装在一个抽象产品类中,每个具体产品类都是抽象产品类的子类;而后提供一个工厂类用于建立各类产品,在工厂类中提供一个建立产品的工厂方法,该方法能够根据所传 入的参数不一样建立不一样的具体产品对象;客户端只需调用工厂类的工厂方法并传入相应的参数便可获得一个产品 对象。java
简单工厂模式定义以下:数据库
简单工厂模式(Simple Factory Pattern):定义一个工厂类,它能够根据参数的不一样返回不一样类的实例,被创 建的实例一般都具备共同的父类。由于在简单工厂模式中用于建立实例的方法是静态(static)方法,所以简单工 厂模式又被称为静态工厂方法(Static Factory Method)模式,它属于类建立型模式。编程
简单工厂模式的要点在于:当你须要什么,只须要传入一个正确的参数,就能够获取你所须要的对象,而无须知道其建立细节。简单工厂模式结构比较简单,其核心是工厂类的设计,其结构如图所示:设计模式
• Factory(工厂角色):工厂角色即工厂类,它是简单工厂模式的核心,负责实现建立全部产品实例的内部逻 辑;工厂类能够被外界直接调用,建立所需的产品对象;在工厂类中提供了静态的工厂方法 factoryMetho d(),它的返回类型为抽象产品类型 Product。函数
• Product(抽象产品角色):它是工厂类所建立的全部对象的父类,封装了各类产品对象的公有方法,它的 引入将提升系统的灵活性,使得在工厂类中只需定义一个通用的工厂方法,由于全部建立的具体产品对象都 是其子类对象。学习
• ConcreteProduct(具体产品角色):它是简单工厂模式的建立目标,全部被建立的对象都充当这个角色的 某个具体类的实例。每个具体产品角色都继承了抽象产品角色,须要实如今抽象产品中声明的抽象方法。ui
在简单工厂模式中,客户端经过工厂类来建立一个产品类的实例,而无须直接使用new关键字来建立对象,它是工厂模式家族中最简单的一员。this
在使用简单工厂模式时,首先须要对产品类进行重构,不能设计一个一应俱全的产品类,而需根据实际状况设计 一个产品层次结构。spa
(1) 工厂类包含必要的判断逻辑,能够决定在何时建立哪个产品类的实例,客户端能够免除直接建立产品对象的职责,而仅仅“消费”产品,简单工厂模式实现了对象建立和使用的分离。
(2) 客户端无须知道所建立的具体产品类的类名,只须要知道具体产品类所对应的参数便可,对于一些复杂的类名,经过简单工厂模式能够在必定程度减小使用者的记忆量。
(3) 经过引入配置文件,能够在不修改任何客户端代码的状况下更换和增长新的具体产品类,在必定程度上提升了 系统的灵活性。
(1) 因为工厂类集中了全部产品的建立逻辑,职责太重,一旦不能正常工做,整个系统都要受到影响。
(2) 使用简单工厂模式势必会增长系统中类的个数(引入了新的工厂类),增长了系统的复杂度和理解难度。
(3) 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能形成工厂逻辑过于复 杂,不利于系统的扩展和维护。
(4) 简单工厂模式因为使用了静态工厂方法,形成工厂角色没法造成基于继承的等级结构。
(1) 工厂类负责建立的对象比较少,因为建立的对象较少,不会形成工厂方法中的业务逻辑太过复杂。
(2) 客户端只知道传入工厂类的参数,对于如何建立对象并不关心。
简单工厂模式虽然简单,但存在一个很严重的问题。当系统中须要引入新产品时,因为静态工厂方法经过所传入参数的不一样来建立不一样的产品,这一定要修改工厂类的源代码,将违背“开闭原则”,如何实现增长新产品而不影响已有代码?工厂方法模式应运而生,本文将介绍第二种工厂模式——工厂方法模式。
在工厂方法模式中,咱们再也不提供一个统一的工厂类来建立全部的产品对象,而是针对不一样的产品提供不一样的工厂,系统提供一个与产品等级结构对应的工厂等级结构。
工厂方法模式(Factory Method Pattern):定义一个用于建立对象的接口,让子类决定将哪个类实例化。工厂方法模式让一个类的实例化延迟到其子类。工厂方法模式又简称为工厂模式(Factory Pattern),又可称做虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic Factory Pattern)。工厂方法模式是一种 类建立型模式。
工厂方法模式提供一个抽象工厂接口来声明抽象工厂方法,而由其子类来具体实现工厂方法,建立具体的产品对象。工厂方法模式结构如图所示:
• Product(抽象产品):它是定义产品的接口,是工厂方法模式所建立对象的超类型,也就是产品对象的公 共父类。
• ConcreteProduct(具体产品):它实现了抽象产品接口,某种类型的具体产品由专门的具体工厂建立,具 体工厂和具体产品之间一一对应。
• Factory(抽象工厂):在抽象工厂类中,声明了工厂方法(Factory Method),用于返回一个产品。抽象工 厂是工厂方法模式的核心,全部建立对象的工厂类都必须实现该接口。
• ConcreteFactory(具体工厂):它是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户 端调用,返回一个具体产品类的实例。
//日志记录器接口:抽象产品 interface Logger { public void writeLog(); } //数据库日志记录器:具体产品 class DatabaseLogger implements Logger { public void writeLog() { System.out.println("数据库日志记录。"); } } //文件日志记录器:具体产品 class FileLogger implements Logger { public void writeLog() { System.out.println("文件日志记录。"); } } //日志记录器工厂接口:抽象工厂 interface LoggerFactory { public Logger createLogger(); } //数据库日志记录器工厂类:具体工厂 class DatabaseLoggerFactory implements LoggerFactory { public Logger createLogger() { //链接数据库,代码省略 //建立数据库日志记录器对象 Logger logger = new DatabaseLogger(); //初始化数据库日志记录器,代码省略 return logger; } } //文件日志记录器工厂类:具体工厂 class FileLoggerFactory implements LoggerFactory { public Logger createLogger() { //建立文件日志记录器对象 Logger logger = new FileLogger(); //建立文件,代码省略 return logger; } }
客户端
class Client { public static void main(String args[]) { LoggerFactory factory; Logger logger; factory = new FileLoggerFactory(); //可引入配置文件实现 logger = factory.createLogger(); logger.writeLog(); } }
实际使用的时候,可使用反射技术
(1) 在工厂方法模式中,工厂方法用来建立客户所须要的产品,同时还向客户隐藏了哪一种具体产品类将被实例化这一细节,用户只须要关心所需产品对应的工厂,无须关心建立细节,甚至无须知道具体产品类的类名。
(2) 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它可以让工厂能够自主肯定建立何种产品对象,而如何建立这个对象的细节则彻底封装在具体工厂内部。工厂方法模式之因此又被称为多态工厂模式,就正是由于全部的具体工厂类都具备同一抽象父类。
(3) 使用工厂方法模式的另外一个优势是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其余的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就能够了,这样,系统 的可扩展性也就变得很是好,彻底符合“开闭原则”。
(1) 在添加新产品时,须要编写新的具体产品类,并且还要提供与之对应的具体工厂类,系统中类的个数将成对增长,在必定程度上增长了系统的复杂度,有更多的类须要编译和运行,会给系统带来一些额外的开销。
(2) 因为考虑到系统的可扩展性,须要引入抽象层,在客户端代码中均使用抽象层进行定义,增长了系统的抽象性和理解难度,且在实现时可能须要用到 DOM、反射等技术,增长了系统的实现难度。
(1) 客户端不知道它所须要的对象的类。在工厂方法模式中,客户端不须要知道具体产品类的类名,只须要知道所对应的工厂便可,具体的产品对象由具体工厂类建立,可将具体工厂类的类名存储在配置文件或数据库中。
(2) 抽象工厂类经过其子类来指定建立哪一个对象。在工厂方法模式中,对于抽象工厂类只须要提供一个建立产品的 接口,而由其子类来肯定具体要建立的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象 将覆盖父类对象,从而使得系统更容易扩展。
1)屏蔽对象的细节,面向对象的规范有一点就是别人知道你的东西越少越好。
2)对象的建立和使用解耦。
系统所提供的工厂生产的具体产品并非一个简单的对象,而是多个位于不一样产品等级结构、属于不一样类型的 具体产品时就可使用抽象工厂模式。抽象工厂模式是全部形式的工厂模式中最为抽象和最具通常性的一种形 式。抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式 须要面对多个产品等级结构,一个工厂等级结构能够负责多个不一样产品等级结构中的产品对象的建立。当一个工厂等级结构能够建立出分属于不一样产品等级结构的一个产品族中的全部对象时,抽象工厂模式比工厂方法模式更 为简单、更有效率。抽象工厂模式示意图如图所示:
在图中,每个具体工厂能够生产属于一个产品族的全部产品,例如生产颜色相同的正方形、圆形和椭圆形,所生产的产品又位于不一样的产品等级结构中。若是使用工厂方法模式,图所示结构须要提供 15 个具体工厂,而使用 抽象工厂模式只须要提供 5 个具体工厂,极大减小了系统中类的个数。
//界面皮肤工厂接口:抽象工厂 interface SkinFactory { public Button createButton(); public TextField createTextField(); public ComboBox createComboBox(); } //Spring皮肤工厂:具体工厂 class SpringSkinFactory implements SkinFactory { public Button createButton() { return new SpringButton(); } public TextField createTextField() { return new SpringTextField(); } public ComboBox createComboBox() { return new SpringComboBox(); } }
(1) 抽象工厂模式隔离了具体类的生成,使得客户并不须要知道什么被建立。因为这种隔离,更换一个具体工厂就变得相对容易,全部的具体工厂都实现了抽象工厂中定义的那些公共接口,所以只需改变具体工厂的实例,就能够在某种程度上改变整个软件系统的行为。
(2) 当一个产品族中的多个对象被设计成一块儿工做时,它可以保证客户端始终只使用同一个产品族中的对象。
(3) 增长新的产品族很方便,无须修改已有系统,符合“开闭原则”。
增长新的产品等级结构麻烦(例如在一族中新添加一个组件),须要对原有系统进行较大的修改,甚至须要修改抽象层代码,这显然会带来较大的不便,违背了“开闭原则”。
(1) 一个系统不该当依赖于产品类实例如何被建立、组合和表达的细节,这对于全部类型的工厂模式都是很重要的,用户无须关心对象的建立过程,将对象的建立和使用解耦。
(2) 系统中有多于一个的产品族,而每次只使用其中某一产品族。能够经过配置文件等方式来使得用户能够动态改 变产品族,也能够很方便地增长新的产品族。
(3) 属于同一个产品族的产品将在一块儿使用,这一约束必须在系统的设计中体现出来。同一个产品族中的产品能够 是没有任何关系的对象,可是它们都具备一些共同的约束,如同一操做系统下的按钮和文本框,按钮与文本框之 间没有直接关系,但它们都是属于某一操做系统的,此时具备一个共同的约束条件:操做系统的类型。
(4) 产品等级结构稳定,设计完成以后,不会向系统中增长新的产品等级结构或者删除已有的产品等级结构。
为了节约系统资源,有时须要确保系统中某个类只有惟一一个实例,当这个惟一实例建立成功以后,咱们没法再建立一个同类型的其余对象,全部的操做都只能基于这个惟一实例。为了确保对象的惟一性,咱们能够经过单例模式来实现,这就是单例模式的动机所在。
原型模式(Prototype Pattern):使用原型实例指定建立对象的种类,而且经过拷贝这些原型建立新的对象。原型模式是一种对象建立型模式。
原型模式的工做原理很简单:将一个原型对象传给那个要发动建立的对象,这个要发动建立的对象经过请求原型对象拷贝本身来实现建立过程。因为在软件系统中咱们常常会遇到须要建立多个相同或者类似对象的状况,所以原型模式在真实开发中的使用频率仍是很是高的。原型模式是一种“另类”的建立型模式,建立克隆对象的工厂就是原型类自身,工厂方法由克隆方法来实现。
须要注意的是经过克隆方法所建立的对象是全新的对象,它们在内存中拥有新的地址,一般对克隆所产生的对象进行修改对原型对象不会形成任何影响,每个克隆对象都是相互独立的。经过不一样的方式修改能够获得一系列类似但不彻底相同的对象。
//工做周报WeeklyLog:具体原型类,考虑到代码的可读性和易理解性,只列出部分与模式相关的核心代码 @Data class WeeklyLog implements Cloneable { private String name; private String date; private String content; //克隆方法clone(),此处使用Java语言提供的克隆机制 public WeeklyLog clone() { Object obj = null; try { obj = super.clone(); return (WeeklyLog) obj; } catch (CloneNotSupportedException e) { System.out.println("不支持复制!"); return null; } } }
在浅克隆中,若是原型对象的成员变量是值类型,将复制一份给克隆对象;若是原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。简单来讲,在浅克隆中,当对象被复制时只复制它自己和其中包含的值类型的成员变量,而引用类型的成员对象并无复制,如图所示:
Object的clone方法使用的就是浅克隆
在深克隆中,不管原型对象的成员变量是值类型仍是引用类型,都将复制一份给克隆对象,深克隆将原型对象的全部引用对象也复制一份给克隆对象。简单来讲,在深克隆中,除了对象自己被复制外,对象所包含的全部成员变量也将复制,如图所示:
在 Java 语言中,若是须要实现深克隆,能够经过序列化(Serialization)等方式来实现。序列化就是将对象写 到流的过程,写到流中的对象是原有对象的一个拷贝,而原对象仍然存在于内存中。经过序列化实现的拷贝不只能够复制对象自己,并且能够复制其引用的成员对象,所以经过序列化将对象写到一个流中,再从流里将其读出来,能够实现深克隆。须要注意的是可以实现序列化的对象其类必须实现 Serializable 接口,不然没法实现序列化操做。下面咱们使用深克隆技术来实现工做周报和附件对象的复制,因为要将附件对象和工做周报对象都写入流中,所以两个类均须要实现 Serializable 接口。
(1) 当建立新的对象实例较为复杂时,使用原型模式能够简化对象的建立过程,经过复制一个已有实例能够提升新 实例的建立效率。
(2) 扩展性较好,因为在原型模式中提供了抽象原型类,在客户端能够针对抽象原型类进行编程,而将具体原型类 写在配置文件中,增长或减小产品类对原有系统都没有任何影响。
(3) 原型模式提供了简化的建立结构,工厂方法模式经常须要有一个与产品类等级结构相同的工厂等级结构,而原 型模式就不须要这样,原型模式中产品的复制是经过封装在原型类中的克隆方法实现的,无须专门的工厂类来创 建产品。
(4) 可使用深克隆的方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来,以便在须要的时 候使用(如恢复到某一历史状态),可辅助实现撤销操做。
(1) 须要为每个类配备一个克隆方法,并且该克隆方法位于一个类的内部,当对已有的类进行改造时,须要修改源代码,违背了“开闭原则”
(2) 在实现深克隆时须要编写较为复杂的代码,并且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来可能会比较麻烦。
建造者模式是较为复杂的建立型模式,它将客户端与包含多个组成部分(或部件)的复杂对象的建立过程分离,客户端无须知道复杂对象的内部组成部分与装配方式,只须要知道所需建造者的类型便可。它关注如何一步一步建立一个的复杂对象,不一样的具体建造者定义了不一样的建立过程,且具体建造者相互独立,增长新的建造者很是方便,无须修改已有代码,系统具备较好的扩展性。
建造者模式定义以下:
建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得一样的构建过程能够建立不一样的 表示。建造者模式是一种对象建立型模式。
建造者模式一步一步建立一个复杂的对象,它容许用户只经过指定复杂对象的类型和内容就能够构建它们,用户不须要知道内部的具体构建细节。建造者模式结构如图所示:
在建造者模式的定义中提到了复杂对象,那么什么是复杂对象?简单来讲,复杂对象是指那些包含多个成员属性的对象,这些成员属性也称为部件或零件,如汽车包括方向盘、发动机、轮胎等部件,电子邮件包括发件人、收件人、主题、内容、附件等部件,一个典型的复杂对象类代码示例以下:在建造者模式的定义中提到了复杂对象,那么什么是复杂对象?简单来讲,复杂对象是指那些包含多个成员属性的对象,这些成员属性也称为部件或零件,如汽车包括方向盘、发动机、轮胎等部件,电子邮件包括发件人、收件人、主题、内容、附件等部件,一个典型的复杂对象类代码示例以下:
package cn.x5456.builderpattern; /** * @author x5456 */ public class BuilderPattern { class Product { private String partA; //定义部件,部件能够是任意类型,包括值类型和引用类型 private String partB; private String partC; public String getPartA() { return partA; } public void setPartA(String partA) { this.partA = partA; } public String getPartB() { return partB; } public void setPartB(String partB) { this.partB = partB; } public String getPartC() { return partC; } public void setPartC(String partC) { this.partC = partC; } } abstract class Builder { //建立产品对象 protected Product product = new Product(); public abstract void buildPartA(); public abstract void buildPartB(); public abstract void buildPartC(); //返回产品对象 public Product getResult() { return product; } } // 指挥者类。在指挥类中控制产品组件构造顺序 // 该类主要有两个做用: // 一方面它隔离了客户与建立过程;(至关于咱们去买电脑,不用咱们组装,而是Director帮咱们组装) // 另外一方面它控制产品的建立过程,包括某个buildPartX() 方法是否被调用以多个 buildPartX() 方法调用的前后次序等。 class Director { private Builder builder; public Director(Builder builder) { this.builder = builder; } public void setBuilder(Builder builder) { this.builder = builder; } //产品构建与组装方法 public Product construct() { builder.buildPartA(); builder.buildPartB(); builder.buildPartC(); return builder.getResult(); } } public void func() { Builder builder = new ConcreteBuilder(); //可经过配置文件+反射实现 Director director = new Director(builder); Product product = director.construct(); } }
在建造者模式中,客户端只需实例化指挥者类,指挥者类针对抽象建造者编程,客户端根据须要传入具体的建造 者类型,指挥者将指导具体建造者一步一步构造一个完整的产品(逐步调用具体建造者的 buildX() 方法),相同 的构造过程能够建立彻底不一样的产品。在游戏角色实例中,若是须要更换角色,只须要修改配置文件,更换具体角色建造者类便可;若是须要增长新角色,能够增长一个新的具体角色建造者类做为抽象角色建造者的子类,再修改配置文件便可,原有代码无须修改,彻底符合“开闭原则”。
abstract class ActorBuilder { protected Actor actor = new Actor(); public abstract void buildType(); public abstract void buildSex(); public abstract void buildFace(); public abstract void buildCostume(); public abstract void buildHairstyle(); public Actor construct() { this.buildType(); this.buildSex(); this.buildFace(); this.buildCostume(); this.buildHairstyle(); return actor; } }
调用方法也要修改:
ActorBuilder ab;
ab = (ActorBuilder)XMLUtil.getBean();
Actor actor;
actor = ab.construct();
建造者模式除了逐步构建一个复杂产品对象外,还能够经过 Director 类来更加精细地控制产品的建立过程,例如增长一类称之为钩子方法(HookMethod)的特殊方法(经过Director)来控制是否对某个 buildPartX() 的调用。
钩子方法的返回类型一般为 boolean 类型,方法名通常为 isXXX(),钩子方法定义在抽象建造者类中。例如咱们 能够在游戏角色的抽象建造者类 ActorBuilder 中定义一个方法 isBareheaded(),用于判断某个角色是否为“光 头(Bareheaded)”,在 ActorBuilder 为之提供一个默认实现,其返回值为 false,代码以下所示:
(1) 在建造者模式中,客户端没必要知道产品内部组成的细节,将产品自己与产品的建立过程解耦,使得相同的建立 过程能够建立不一样的产品对象。
(2) 每个具体建造者都相对独立,而与其余的具体建造者无关,所以能够很方便地替换具体建造者或增长新的具 体建造者,用户使用不一样的具体建造者便可获得不一样的产品对象。因为指挥者类针对抽象建造者编程,增长新的具体建造者无须修改原有类库的代码,系统扩展方便,符合“开闭原则”
(3) 能够更加精细地控制产品的建立过程。将复杂产品的建立步骤分解在不一样的方法中,使得建立过程更加清晰,也更方便使用程序来控制建立过程。
(1) 建造者模式所建立的产品通常具备较多的共同点,其组成部分类似,若是产品之间的差别性很大,例如不少组成部分都不相同,不适合使用建造者模式,所以其使用范围受到必定的限制。
(2) 若是产品的内部变化复杂,可能会致使须要定义不少具体建造者类来实现这种变化,致使系统变得很庞大,增长系统的理解难度和运行成本。
(1) 须要生成的产品对象有复杂的内部结构,这些产品对象一般包含多个成员属性。
(2) 须要生成的产品对象的属性相互依赖,须要指定其生成顺序。
(3) 对象的建立过程独立于建立该对象的类。在建造者模式中经过引入了指挥者类,将建立过程封装在指挥者类中,而不在建造者类和客户类中。
(4) 隔离复杂对象的建立和使用,并使得相同的建立过程能够建立不一样的产品。