1、单例设计模式
单例模式的定义与特点
单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。
单例模式的特点:
-
单例类只有一个实例对象;
-
该单例对象必须由单例类自行创建;
-
单例类对外提供一个访问该单例的全局访问点。
优点:
-
单例模式可以保证内存里只有一个实例,减少了内存的开销。
-
可以避免对资源的多重占用。
-
单例模式设置全局访问点,可以优化和共享资源的访问。
缺点:
-
单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
-
在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。
-
单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。
单例模式的应用场景
-
需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少 GC。
-
某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。
-
某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。
-
某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
-
频繁访问数据库或文件的对象。
-
对于一些控制硬件级别的操作,或者从系统上来讲应当是单一控制逻辑的操作,如果有多个实例,则系统会完全乱套。
-
当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。
单例模式的结构与实现
外部类可以通过“new 构造函数()”来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。
实现:
第一种: 懒汉式
该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例.
如果编写的是多线程程序,则不要删除上例代码中的关键字 volatile 和 synchronized,否则将存在线程非安全的问题。如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点
public class LazySingleton { private static volatile LazySingleton instance = null; //保证 instance 在所有线程中同步 private LazySingleton() { } //private 避免类在外部被实例化 public static synchronized LazySingleton getInstance() { //getInstance 方法前加同步 if (instance == null) { instance = new LazySingleton(); } return instance; } }
第二种: 饿汉式
该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。
饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题
public class HungrySingleton { private static final HungrySingleton instance = new HungrySingleton(); private HungrySingleton() { } public static HungrySingleton getInstance() { return instance; } }
应用实例:
【例1】用懒汉式单例模式模拟产生美国当今总统对象。
public class SingletonLazy { public static void main(String[] args) { President zt1 = President.getInstance(); zt1.getName(); //输出总统的名字 President zt2 = President.getInstance(); zt2.getName(); //输出总统的名字 if (zt1 == zt2) { System.out.println("他们是同一人!"); } else { System.out.println("他们不是同一人!"); } } } class President { private static volatile President instance = null; //保证instance在所有线程中同步 //private避免类在外部被实例化 private President() { System.out.println("只能产生一个总统!"); } public static synchronized President getInstance() { //在getInstance方法上加同步 if (instance == null) { instance = new President(); } else { System.out.println("已经有一个总统,不能产生新总统!"); } return instance; } public void getName() { System.out.println("我是美国总统:特朗普。"); } }
2、原型设计模式
原型模式的定义与特点
用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。
优点:
-
Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
-
可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
缺点:
-
需要为每一个类都配置一个 clone 方法
-
clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
-
当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
原型模式的结构与实现
结构:
-
抽象原型类:规定了具体原型对象必须实现的接口。
-
具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
-
访问类:使用具体原型类中的 clone() 方法来复制新的对象。
实现:
//具体原型类 class Realizetype implements Cloneable { Realizetype() { System.out.println("具体原型创建成功!"); } public Object clone() throws CloneNotSupportedException { System.out.println("具体原型复制成功!"); return (Realizetype) super.clone(); } } //原型模式的测试类 public class PrototypeTest { public static void main(String[] args) throws CloneNotSupportedException { Realizetype obj1 = new Realizetype(); Realizetype obj2 = (Realizetype) obj1.clone(); System.out.println("obj1==obj2?" + (obj1 == obj2)); } }
应用实例:
【例】用原型模式生成“三好学生”奖状
public class ProtoTypeCitation { public static void main(String[] args) throws CloneNotSupportedException { citation obj1 = new citation("张三", "同学:在2016学年第一学期中表现优秀,被评为三好学生。", "韶关学院"); obj1.display(); citation obj2 = (citation) obj1.clone(); obj2.setName("李四"); obj2.display(); } } //奖状类 class citation implements Cloneable { String name; String info; String college; citation(String name, String info, String college) { this.name = name; this.info = info; this.college = college; System.out.println("奖状创建成功!"); } void setName(String name) { this.name = name; } String getName() { return (this.name); } void display() { System.out.println(name + info + college); } public Object clone() throws CloneNotSupportedException { System.out.println("奖状拷贝成功!"); return (citation) super.clone(); } }
【例】用带原型管理器的原型模式来生成包含“圆”和“正方形”等图形的原型,并计算其面积。
import java.util.*; interface Shape extends Cloneable { public Object clone(); //拷贝 public void countArea(); //计算面积 } class Circle implements Shape { public Object clone() { Circle w = null; try { w = (Circle) super.clone(); } catch (CloneNotSupportedException e) { System.out.println("拷贝圆失败!"); } return w; } public void countArea() { int r = 0; System.out.print("这是一个圆,请输入圆的半径:"); Scanner input = new Scanner(System.in); r = input.nextInt(); System.out.println("该圆的面积=" + 3.1415 * r * r + "\n"); } } class Square implements Shape { public Object clone() { Square b = null; try { b = (Square) super.clone(); } catch (CloneNotSupportedException e) { System.out.println("拷贝正方形失败!"); } return b; } public void countArea() { int a = 0; System.out.print("这是一个正方形,请输入它的边长:"); Scanner input = new Scanner(System.in); a = input.nextInt(); System.out.println("该正方形的面积=" + a * a + "\n"); } } class ProtoTypeManager { private HashMap<String, Shape> ht = new HashMap<String, Shape>(); public ProtoTypeManager() { ht.put("Circle", new Circle()); ht.put("Square", new Square()); } public void addshape(String key, Shape obj) { ht.put(key, obj); } public Shape getShape(String key) { Shape temp = ht.get(key); return (Shape) temp.clone(); } } public class ProtoTypeShape { public static void main(String[] args) { ProtoTypeManager pm = new ProtoTypeManager(); Shape obj1 = (Circle) pm.getShape("Circle"); obj1.countArea(); Shape obj2 = (Shape) pm.getShape("Square"); obj2.countArea(); } }
3.1简单工厂模式
现实生活中,原始社会自给自足(没有工厂),农耕社会小作坊(简单工厂,民间酒坊),工业革命流水线(工厂方法,自产自销),现代产业链代工厂(抽象工厂,富士康)。我们的项目代码同样是由简到繁一步一步迭代而来的,但对于调用者来说,却越来越简单。
在日常开发中,凡是需要生成复杂对象的地方,都可以尝试考虑使用工厂模式来代替。
注意:上述复杂对象指的是类的构造函数参数过多等对类的构造有影响的情况,因为类的构造过于复杂,如果直接在其他业务类内使用,则两者的耦合过重,后续业务更改,就需要在任何引用该类的源代码内进行更改,光是查找所有依赖就很消耗时间了,更别说要一个一个修改了。
工厂模式的定义与特点
定义:
定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。
被创建的对象称为“产品”,把创建产品的对象称为“工厂”
简单工厂:
-
如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫“简单工厂模式”。
-
在简单工厂模式中创建实例的方法通常为静态方法,因此简单工厂模式又叫作静态工厂方法模式
-
简单来说,简单工厂模式有一个具体的工厂类,可以生成多个不同的产品,属于创建型设计模式。简单工厂模式不在 GoF 23 种设计模式之列。
-
简单工厂模式每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度,违背了“开闭原则”
优点:
-
工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。
-
客户端无需知道所创建具体产品的类名,只需知道参数即可。
-
也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。
缺点:
-
简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则。
-
使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度
-
系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂
-
简单工厂模式使用了 static 工厂方法,造成工厂角色无法形成基于继承的等级结构。
简单工厂应用场景
-
对于产品种类相对较少的情况,考虑使用简单工厂模式。
-
使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑。
简单工厂的结构与实现
主要角色:
-
抽象产品(Product):是简单工厂创建的所有对象的父类,负责描述所有实例共有的公共接口。
-
简单工厂(SimpleFactory):是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
-
具体产品(ConcreteProduct):是简单工厂模式的创建目标。
public class Client { public static void main(String[] args) { } //抽象产品 public interface Product { void show(); } //具体产品:ProductA static class ConcreteProduct1 implements Product { public void show() { System.out.println("具体产品1显示..."); } } //具体产品:ProductB static class ConcreteProduct2 implements Product { public void show() { System.out.println("具体产品2显示..."); } } final class Const { static final int PRODUCT_A = 0; static final int PRODUCT_B = 1; static final int PRODUCT_C = 2; } //简单工厂 static class SimpleFactory { public static Product makeProduct(int kind) { switch (kind) { case Const.PRODUCT_A: return new ConcreteProduct1(); case Const.PRODUCT_B: return new ConcreteProduct2(); } return null; } } }
3.2工厂方法模式
“工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。
工厂方法模式 只考虑生产同等级的产品
工厂方法模式的特点
优点:
-
用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
-
灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
-
典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。
缺点:
-
类的个数容易过多,增加复杂度
-
增加了系统的抽象性和理解难度
-
抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决。
工厂方法应用场景:
-
客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
-
创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
-
客户不关心创建产品的细节,只关心产品的品牌
工厂方法的结构与实现
结构:
-
抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
-
具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
-
抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。
-
具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
package FactoryMethod; //抽象产品:提供了产品的接口 interface Product { public void show(); } //具体产品1:实现抽象产品中的抽象方法 class ConcreteProduct1 implements Product { public void show() { System.out.println("具体产品1显示..."); } } //具体产品2:实现抽象产品中的抽象方法 class ConcreteProduct2 implements Product { public void show() { System.out.println("具体产品2显示..."); } } //抽象工厂:提供了产品的生成方法 interface AbstractFactory { public Product newProduct(); } //具体工厂1:实现了厂品的生成方法 class ConcreteFactory1 implements AbstractFactory { public Product newProduct() { System.out.println("具体工厂1生成-->具体产品1..."); return new ConcreteProduct1(); } } //具体工厂2:实现了厂品的生成方法 class ConcreteFactory2 implements AbstractFactory { public Product newProduct() { System.out.println("具体工厂2生成-->具体产品2..."); return new ConcreteProduct2(); } } public class AbstractFactoryTest { public static void main(String[] args) { try { Product a; AbstractFactory af; af = (AbstractFactory) ReadXML1.getObject(); a = af.newProduct(); a.show(); } catch (Exception e) { System.out.println(e.getMessage()); } } }
package FactoryMethod; import javax.xml.parsers.*; import org.w3c.dom.*; import java.io.*; class ReadXML1 { //该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象 public static Object getObject() { try { //创建文档对象 DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = dFactory.newDocumentBuilder(); Document doc; doc = builder.parse(new File("src/FactoryMethod/config1.xml")); //获取包含类名的文本节点 NodeList nl = doc.getElementsByTagName("className"); Node classNode = nl.item(0).getFirstChild(); String cName = "FactoryMethod." + classNode.getNodeValue(); //System.out.println("新类名:"+cName); //通过类名生成实例对象并将其返回 Class<?> c = Class.forName(cName); Object obj = c.newInstance(); return obj; } catch (Exception e) { e.printStackTrace(); return null; } } }
xml文件
<?xml version="1.0" encoding="UTF-8"?> <config> <className>ConcreteFactory1</className> </config>
应用实例:
【例】用工厂方法模式设计畜牧场,客户端程序通过对象生成器类 ReadXML2 读取 XML 配置文件中的数据来决定养马还是养牛
package FactoryMethod; import java.awt.*; import javax.swing.*; public class AnimalFarmTest { public static void main(String[] args) { try { Animal a; AnimalFarm af; af = (AnimalFarm) ReadXML2.getObject(); a = af.newAnimal(); a.show(); } catch (Exception e) { System.out.println(e.getMessage()); } } } //抽象产品:动物类 interface Animal { public void show(); } //具体产品:马类 class Horse implements Animal { JScrollPane sp; JFrame jf = new JFrame("工厂方法模式测试"); public Horse() { Container contentPane = jf.getContentPane(); JPanel p1 = new JPanel(); p1.setLayout(new GridLayout(1, 1)); p1.setBorder(BorderFactory.createTitledBorder("动物:马")); sp = new JScrollPane(p1); contentPane.add(sp, BorderLayout.CENTER); JLabel l1 = new JLabel(new ImageIcon("src/A_Horse.jpg")); p1.add(l1); jf.pack(); jf.setVisible(false); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //用户点击窗口关闭 } public void show() { jf.setVisible(true); } } //具体产品:牛类 class Cattle implements Animal { JScrollPane sp; JFrame jf = new JFrame("工厂方法模式测试"); public Cattle() { Container contentPane = jf.getContentPane(); JPanel p1 = new JPanel(); p1.setLayout(new GridLayout(1, 1)); p1.setBorder(BorderFactory.createTitledBorder("动物:牛")); sp = new JScrollPane(p1); contentPane.add(sp, BorderLayout.CENTER); JLabel l1 = new JLabel(new ImageIcon("src/A_Cattle.jpg")); p1.add(l1); jf.pack(); jf.setVisible(false); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //用户点击窗口关闭 } public void show() { jf.setVisible(true); } } //抽象工厂:畜牧场 interface AnimalFarm { public Animal newAnimal(); } //具体工厂:养马场 class HorseFarm implements AnimalFarm { public Animal newAnimal() { System.out.println("新马出生!"); return new Horse(); } } //具体工厂:养牛场 class CattleFarm implements AnimalFarm { public Animal newAnimal() { System.out.println("新牛出生!"); return new Cattle(); } }
package FactoryMethod; import javax.xml.parsers.*; import org.w3c.dom.*; import java.io.*; class ReadXML2 { public static Object getObject() { try { DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = dFactory.newDocumentBuilder(); Document doc; doc = builder.parse(new File("src/main/java/FactoryMethod/config1.xml")); NodeList nl = doc.getElementsByTagName("className"); Node classNode = nl.item(0).getFirstChild(); String cName = "FactoryMethod." + classNode.getNodeValue(); System.out.println("新类名:" + cName); Class<?> c = Class.forName(cName); Object obj = c.newInstance(); return obj; } catch (Exception e) { e.printStackTrace(); return null; } } }
xml文件
<?xml version="1.0" encoding="UTF-8"?> <config> <className>CattleFarm</className> </config>
3.3 抽象工厂模式
抽象工厂模式将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族, 工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品
抽象工厂的定义与特点
是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
优点:
-
可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
-
当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
-
抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。
缺点:
当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。
使用抽象工厂模式一般要满足以下条件:
-
系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
-
系统一次只可能消费其中某一族产品,即同族的产品一起使用。
抽象工厂的结构与实现
由抽象工厂、具体工厂、抽象产品和具体产品等 4 个要素构成,但抽象工厂中方法个数不同,抽象产品的个数也不同。
结构:
-
抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
-
具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
-
抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
-
具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
实现:
抽象工厂模式的结构同工厂方法模式的结构相似,不同的是其产品的种类不止一个,所以创建产品的方法也不止一个
(1) 抽象工厂:提供了产品的生成方法
interface AbstractFactory { public Product1 newProduct1(); public Product2 newProduct2(); }
(2) 具体工厂:实现了产品的生成方法
class ConcreteFactory1 implements AbstractFactory { public Product1 newProduct1() { System.out.println("具体工厂 1 生成-->具体产品 11..."); return new ConcreteProduct11(); } public Product2 newProduct2() { System.out.println("具体工厂 1 生成-->具体产品 21..."); return new ConcreteProduct21(); } }
应用实例:
【例】用抽象工厂模式设计农场类:
用抽象工厂模式来设计两个农场,一个是韶关农场用于养牛和种菜,一个是上饶农场用于养马和种水果,可以在以上两个农场中定义一个生成动物的方法 newAnimal() 和一个培养植物的方法 newPlant()
package AbstractFactory; import java.awt.*; import javax.swing.*; public class FarmTest { public static void main(String[] args) { try { Farm f; Animal a; Plant p; f = (Farm) ReadXML.getObject(); a = f.newAnimal(); p = f.newPlant(); a.show(); p.show(); } catch (Exception e) { System.out.println(e.getMessage()); } } } //抽象产品:动物类 interface Animal { public void show(); } //具体产品:马类 class Horse implements Animal { public void show() { System.out.println("生产了一匹马!"); } } //具体产品:牛类 class Cattle implements Animal { public void show() { System.out.println("生产了一头牛!"); } } //抽象产品:植物类 interface Plant { public void show(); } //具体产品:水果类 class Fruitage implements Plant { public void show() { System.out.println("水果结果了!"); } } //具体产品:蔬菜类 class Vegetables implements Plant { public void show() { System.out.println("蔬菜成熟了!"); } } //抽象工厂:农场类 interface Farm { public Animal newAnimal(); public Plant newPlant(); } //具体工厂:韶关农场类 class SGfarm implements Farm { public Animal newAnimal() { System.out.println("新牛出生"); return new Cattle(); } public Plant newPlant() { System.out.println("蔬菜长成"); return new Vegetables(); } } //具体工厂:上饶农场类 class SRfarm implements Farm { public Animal newAnimal() { System.out.println("新马出生"); return new Horse(); } public Plant newPlant() { System.out.println("水果长成"); return new Fruitage(); } }
package AbstractFactory; import javax.xml.parsers.*; import org.w3c.dom.*; import java.io.*; class ReadXML { public static Object getObject() { try { DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = dFactory.newDocumentBuilder(); Document doc; doc = builder.parse(new File("src/main/java/AbstractFactory/config1.xml")); NodeList nl = doc.getElementsByTagName("className"); Node classNode = nl.item(0).getFirstChild(); String cName = "AbstractFactory." + classNode.getNodeValue(); System.out.println("新类名:" + cName); Class<?> c = Class.forName(cName); Object obj = c.newInstance(); return obj; } catch (Exception e) { e.printStackTrace(); return null; } } }
<?xml version="1.0" encoding="UTF-8"?> <config> <className>SGfarm</className> </config>
4 建造者模式
创建一个复杂的对象,这个复杂对象通常由多个子部件按一定的步骤组合而成。
建造者与工厂模式的不同
关注点不同:建造者模式注重零部件的组装过程,而工厂方法模式更注重零部件的创建过程,但两者可以结合使用。
建造者模式的定义与特点
定义:
指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的
优点:
-
封装性好,构建和表示分离。
-
扩展性好,各个具体的建造者相互独立,有利于系统的解耦。
-
客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险
缺点:
-
产品的组成部分必须相同,这限制了其使用范围。
-
如果产品的内部变化复杂,如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大。
建造者模式的结构与实现
结构:
-
产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个零部件。
-
抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。
-
具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
-
指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。
实现:
(1) 产品角色:包含多个组成部件的复杂对象。
package BuilderTest; class Product { private String partA; private String partB; private String partC; public void setPartA(String partA) { this.partA = partA; } public void setPartB(String partB) { this.partB = partB; } public void setPartC(String partC) { this.partC = partC; } public void show() { //显示产品的特性 } }
(2) 抽象建造者:包含创建产品各个子部件的抽象方法。
package BuilderTest; 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; } }
(3) 具体建造者:实现了抽象建造者接口。
package BuilderTest; public class ConcreteBuilder extends Builder { public void buildPartA() { product.setPartA("建造 PartA"); } public void buildPartB() { product.setPartB("建造 PartB"); } public void buildPartC() { product.setPartC("建造 PartC"); } }
(4) 指挥者:调用建造者中的方法完成复杂对象的创建。
package BuilderTest; class Director { private Builder builder; public Director(Builder builder) { this.builder = builder; } //产品构建与组装方法 public Product construct() { builder.buildPartA(); builder.buildPartB(); builder.buildPartC(); return builder.getResult(); } }
(5) 客户类。
package BuilderTest; public class Client { public static void main(String[] args) { Builder builder = new ConcreteBuilder(); Director director = new Director(builder); Product product = director.construct(); product.show(); } }
应用实例
【例】用建造者模式描述客厅装修,包含墙体的装修、电视机的选择、沙发的购买与布局。
-
具体装修工人是具体建造者,他们负责装修与墙、电视和沙发的布局。
-
项目经理是指挥者,他负责指挥装修工人进行装修。
package Builder; import java.awt.*; import javax.swing.*; public class ParlourDecorator { public static void main(String[] args) { try { Decorator d; d = (Decorator) ReadXML.getObject(); ProjectManager m = new ProjectManager(d); Parlour p = m.decorate(); p.show(); } catch (Exception e) { System.out.println(e.getMessage()); } } } //产品:客厅 class Parlour { private String wall; //墙 private String TV; //电视 private String sofa; //沙发 public void setWall(String wall) { this.wall = wall; } public void setTV(String TV) { this.TV = TV; } public void setSofa(String sofa) { this.sofa = sofa; } public void show() { System.out.println("客厅建造完毕!"); } } //抽象建造者:装修工人 abstract class Decorator { //创建产品对象 protected Parlour product = new Parlour(); public abstract void buildWall(); public abstract void buildTV(); public abstract void buildSofa(); //返回产品对象 public Parlour getResult() { return product; } } //具体建造者:具体装修工人1 class ConcreteDecorator1 extends Decorator { public void buildWall() { product.setWall("w1"); System.out.println("工人1建造了墙"); } public void buildTV() { product.setTV("TV1"); System.out.println("工人1建造了电视"); } public void buildSofa() { product.setSofa("sf1"); System.out.println("工人1建造了沙发"); } } //具体建造者:具体装修工人2 class ConcreteDecorator2 extends Decorator { public void buildWall() { product.setWall("w2"); System.out.println("工人2建造了墙"); } public void buildTV() { product.setTV("TV2"); System.out.println("工人2建造了电视"); } public void buildSofa() { product.setSofa("sf2"); System.out.println("工人2建造了沙发"); } } //指挥者:项目经理 class ProjectManager { private Decorator builder; public ProjectManager(Decorator builder) { this.builder = builder; } //产品构建与组装方法 public Parlour decorate() { builder.buildWall(); builder.buildTV(); builder.buildSofa(); return builder.getResult(); } }
package Builder; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.File; class ReadXML { public static Object getObject() { try { DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = dFactory.newDocumentBuilder(); Document doc; doc = builder.parse(new File("src/main/java/Builder/config1.xml")); NodeList nl = doc.getElementsByTagName("className"); Node classNode = nl.item(0).getFirstChild(); String cName = "Builder." + classNode.getNodeValue(); System.out.println("新类名:" + cName); Class<?> c = Class.forName(cName); Object obj = c.newInstance(); return obj; } catch (Exception e) { e.printStackTrace(); return null; } } }
<?xml version="1.0" encoding="UTF-8"?> <config> <className>ConcreteDecorator1</className> </config>