Java 23种设计模式详尽分析与实例解析之一--创建型模式
面向对象的设计原则
常用的面向对象设计原则包括7个,这些原则并不是独立存在的,它们相互依赖、互为补充。
Java设计模式
创建型模式
简单工厂模式
模式动机:
考虑一个简单的软件应用场景,一个软件系统可以提供多个外观不同的按钮(如圆形按钮、矩形按钮、菱形按钮等),这些按钮都源自同一个基类,不过在继承基类后不同的子类修改了部分属性从而使得它们可以呈现不同的外观,如果我们希望在使用这些按钮时,不需要知道这些具体按钮类的名字,只需要知道表示该按钮类的一个参数,并提供一个调用方便的方法,把该参数传入方法即可返回一个相应的按钮对象,此时,就可以使用简单工厂模式。
模式定义:
又称为静态工厂方法(StaticFactoryMethod)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
模式结构:
模式分析:
当你需要什么,只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节。这种模式将对象的创建和对象业务处理分离,可以降低系统的耦合度,使得两者修改起来都相对容易。
模式应用:
1、 java类库的java.text.DateFormatr类,用于格式化一个本地日期或者时间。
2、 Spring中的对象获取,beanFactory.getBean()方法,用于获得一个对象。
工厂方法模式
模式动机:在简单工厂模式中,只提供了一个工厂类,该工厂类处于对产品类进行实例化的中心位置,它知道每一个产品对象的创建细节,并决定何时实例化哪一个产品类。简单最大的缺点是当有新产品要加入到系统中时,必须修改工厂类,这违反了“开闭原则”。在简单工厂模式中,所有的产品都是由同一个工厂创建,工厂类职责较重,具体产品与工厂类之间的耦合度高,严重影响了系统的灵活性和扩展性。
模式定义:
工厂方法模式(FactoryMethod Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产
品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
模式结构:
模式分析:在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责哪一个产品类被实例化这种细节,使得工厂方法模式允许系统在不修改工厂角色的情况下引进新产品。当系统扩展需要添加新的产品对象时,仅仅需要添加一个具体产品对象以及一个具体工厂对象,原有工厂对象不需要进行任何修改,也不需要修改客户端,很好地符合了“开闭原则”。而简单工厂方法模式在添加新产品对象后不得工厂方法,扩展性不好。工厂方法模式退化可以演变成简单工厂模式。
模式应用:
1、 java.util.Collection接口的迭代器iterator()方法。
2、 java消息服务
3、 JDBC中的工厂方法。
抽象工厂模式
模式动机:在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法也具有唯一性,一般情况下,一个具体工厂中只有一个工厂方法或者一组重载的工厂方法。但是有时候我们需要一个工厂可以提供多个产品对象,而不是单一的产品对象。
为了更清晰地理解工厂方法模式,需要先引入两个概念:
• 产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了 一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
• 产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于海尔电器的产品族中,海尔电冰箱位于电冰箱产品等级结构中。
当系统所提供的工厂所需生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构中属于不同类型的具体产品时需要使用抽象工厂模式。
抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式与工厂方法模式最大的区别在于,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构,一个工厂等级结构可以负责多个不同产品等级结构中的产品对象的创建。当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、有效率。
模式定义:抽象工厂模式(Abstract Factory Pattern)提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。
模式结构:
模式分析:在抽象工厂模式中,每一个具体工厂都提供了多个工厂方法用于生产多种不同类型的产品,这些产品构成了一个产品族。由上图可以知道,抽象工厂模式包含如下角色:
• AbstractFactory,抽象工厂:它声明了一组用于创建一族产品的方法,每一个方法对应一种产品。
• ConcreteFactory,具体工厂:它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。
• AbstractProduct,抽象产品:它为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法。
• Product,具体产品:它定义具体工厂生产的具体产品对象,实现在抽象产品接口中声明的业务和方法。
例如,有三种操作系统,windows、UNIX、Linux,它们三个工厂,它们都具有能够产生两种产品(两个产品族)的能力,一个是文本,一个是按钮,则其抽象工厂模式如下图所示:
模式应用:在以下情况下可以使用抽象工厂模式:
• 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。
• 系统中有多于一个的产品族,而每次只使用其中某一产品族。
• 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。
• 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
(1) Java SE AWT(抽象窗口工具包)
在Java语言的AWT(抽象窗口工具包)中就使用了抽象工厂模式,它使用抽象工厂模式来实现在不同的操作系统中应用程序呈现与所在操作系统一致的外观界面。
(2) 在很多软件系统中需要更换界面主题,要求界面中的按钮、文本框、背景色等一起发生改变时,可以使用抽象工厂模式进行设计。
建造者模式
模式动机:在软件开发中,也存在大量类似汽车一样的复杂对象,它们拥有一系列成员属性,这些成员属性中有些是引用类型的成员对象。而且在这些复杂对象中,还可能存在一些限制条件,如某些属性没有赋值则复杂对象不能作为一个完整的产品使用;有些属性的赋值必须按照某个
顺序,一个属性没有赋值之前,另一个属性可能无法赋值等。复杂对象相当于一辆有待建造的汽车,而对象的属性相当于汽车的部件,建造产品的过程就相当于组合部件的过程。由于组合部件的过程很复杂,因此,这些部件的组合过程往往被“外部化”到一个称作建造者的对象里,建造者返还给客户端的是一个已经建造完毕的完整产品对象,而用户无须关心该对象所包含的属性以及它们的组装方式。比如去KFC点餐,你只需要告诉服务员你需要什么,比如可乐、鸡排汉堡和薯条(视为三个简单对象),此时服务员则把各种对象构造成一个套餐(复杂对象)送到你面前。
模式定义:建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一步一步创建一个复杂的对象,它允许用
户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。建造者模式属于对象创建型模式。根据中文翻译的不同,建造者模式又可以称为生成器模式。
模式结构:
模式分析:如上所示,建造者模式包含如下角色:Builder建造者,ConcreteBuilder具体建造者,Director指挥者,Product产品角色。比如KFC点餐案例中,生产可乐、汉堡、薯条的师傅即为具体建造者,服务员为指挥者,可乐、汉堡、薯条即为产品角色。
模式应用:在下列情况下考虑使用建造者模式:
1、 需要生产的产品对象有复杂的内部结构,这些产品对象通常包含多个成员变量。比如RPG网络游戏中的游戏角色。游戏角色是一个复杂对象。
2、 需要生成的产品对象的属性相互依赖,需要指定其生成顺序。比如要JavaMail,需要一步一步构建一个完整的邮件对象,然后发送。
3、 对象的创建过程独立于创建该对象的类,在建造者模式中通过引入指挥者类,将创建过程封装在指挥者类中,而不再创建者类和客户类中。比如KFC点餐的模拟过程。
4、 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。比如RPG网络游戏中的游戏角色。
原型模式
模式动机:在面向对象系统中,使用原型模式来复制一个对象自身,从而克隆出多个与原型对象一模一样的对象。在软件系统中,有些对象的创建过程较为复杂,而且有时候需要频繁创建,原型模式通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的办法创建出更多同类型的对象,这就是原型模式的意图所在。
模型定义:用原型实例指定对象创建的种类,并且通过拷贝这些原型对象来创建新的对象。
模型结构:
模型分析:在原型模式结构中定义了一个抽象原型类,所有的Java类都继承自java.lang.Object,而Object类提供一个clone()方法,可以将一个Java对象复制一份。因此在Java中可以直接使用Object提供的clone()方法来实现对象的克隆,Java语言中的原型模式实现很简单。能够实现克隆的Java类必须实现一个标识接口Cloneable,表示这个Java类支持复制。如果一个类没有实现这个接口但是调用了clone()方法,Java编译器将抛出一个
CloneNotSupportedException异常
模型应用:
1、 每次创建一个对象需要花费大量时间,原型模式是最好的解决方案,比如:Ctrl+C和Ctrl+V操作。
2、 Struts2中为了保证线程安全性,Action对象的创建使用了原型模式,访问一个已经存在的Action时将通过克隆的方式创建出一个新的对象。从而保证其中定义的变量无须进行加锁实现同步,每一个Action中都有自己的成员变量,避免Struts1因使用单例模式而导致的并发和同步问题。
3、 Spring中,用户也可以采用原型来创建新的bean实例,从而实现每次获取的是通过克隆生成的新实例,对其进行修改时对原有实例对象不造成任何影响。
单例模式
模式动机:如何保证一个类只有一个实例并且这个实例易于被访问呢?定义一个全局变量可以确保对象随时都可以被访问,但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。
模式定义:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。单例模式是一种对象创建型模式。单例模式又名单件模式或单态模式。
模式结构:
模式分析:单例模式的目的是保证一个类仅有一个实例,并提供一个访问它的全局访问点。单例模式包含的角色只有一个,就是单例类——Singleton。单例类拥有一个私有构造函数,确保用户无法通过new关键字直接实例化它。除此之外,该模式中包含一个静态私有成员变量与静态公有的工厂方法,该工厂方法负责检验实例的存在性并实例化自己,然后存储在静态成员变量中,以确保只有一个实例被创建。
懒汉式和饿汉式
模式应用:
1、 java.lang.Runtime类
2、 默认情况下,Spring会通过单例模式创建bean实例。<beanid=”date” class=”java.util.Date” scope=”singleton”>
参考文献:
1、大话设计模式
2、设计模式的艺术之道--软件开发人员内功修炼之道。