【Java 创建型设计模式】五种创建型设计模式详解

 
愿你如阳光,明媚不忧伤。

 


1. 创建型(Creation)模式的特点和分类

 

创建型模式 (Creation Pattern)的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节,对象的创建由相关的工厂来完成。就像我们去商场购买商品时,不需要知道商品是怎么生产出来一样,因为它们由专门的厂商生产。

  • 结构型模式总览
范围 \ 目的创建型模式 5
类模式 1工厂方法
对象模式 4单例
原型
抽象工厂
建造者
  1. 单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
  2. 原型(Prototype)模式:将一个对象作为原型,通过对其进行复制、克隆出多个和原型类似的新实例。
  3. 工厂方法(Factory Method)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
  4. 抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
  5. 建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。

 


2. 单例模式

单例模式(Singleton)指一个类只有一个对象实例,且该类能自行创建这个实例,并只提供一个全局访问点供外部获取该实例的模式(静态方法),其拓展是有限多例模式。
例如:Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。

单例模式的优点和缺点
  • 优点
  1. 单例模式可以保证内存里只有一个实例,减少了内存的开销。
  2. 可以避免对资源的多重占用。
  3. 单例模式设置全局访问点,可以优化和共享资源的访问。
  • 缺点
  1. 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
  2. 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。
  3. 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。
单例模式的应用场景
  1. 需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少 GC。
  2. 某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等。
  3. 某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用。
  4. 某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
  5. 频繁访问数据库或文件的对象(数据源,session工厂等)。
  6. 对于一些控制硬件级别的操作,或者从系统上来讲应当是单一控制逻辑的操作,如果有多个实例,则系统会完全乱套。
  7. 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。
单例模式的结构

构造器私有化,自己聚合自己,自行创建实例,向外暴露一个静态的公共方法 getInstance()

单例类Singleton -instance : Singleton -Singleton() +getInstance() : Singleton 访问类Client #instance : Singleton
单例模式的实现
  • 懒汉式单例
    类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例
/*注意:
如果编写的是多线程程序,则不要删除代码中的关键字 volatile synchronized,否则将存在线程非安全的问题。
如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。*/
public class LazySingleton {
	//保证 instance 在所有线程中同步
    private static volatile LazySingleton instance = null;    
	
	//private 避免类在外部被实例化
    private LazySingleton() {
    }    

   	 //getInstance 方法前加同步
	 public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}
  • 饿汉式单例
    在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题。但如果程序从始至终没有使用过该实例,会造成内存浪费。
public class HungrySingleton {
    private static final HungrySingleton instance = new HungrySingleton();

    private HungrySingleton() {
    }

    public static HungrySingleton getInstance() {
        return instance;
    }
}
  • 双重检查单例
    多线程开发中常使用,线程安全,延迟加载,效率较高,推荐使用
public class DoubleCheckSingleton {
    // 保证 instance 在所有线程中同步
    private static volatile DoubleCheckSingleton instance;

    // private 避免类在外部被实例化
    private DoubleCheckSingleton() {
    }

    // getInstance 方法前加同步和双重检查代码,解决线程安全问题,同时解决懒加载问题,保证效率
    public static synchronized DoubleCheckSingleton getInstance() {
        if (instance == null) {
            synchronized (DoubleCheckSingleton.class) {
                if (instance == null) {
                    instance = new DoubleCheckSingleton();
                }
            }
        }
        return instance;
    }
}

  • 静态内部类
    采用了类装载机制来保证初始化实例时只有一个线程。类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程无法进入的。延迟加载,效率高,推荐使用
public class InnerClassSingleton {
    // 保证 instance 在所有线程中同步
    private static volatile InnerClassSingleton instance;

    // private 构造器私有化
    private InnerClassSingleton() {
    }

    // 静态内部类,该类中有一个静态属性 InnerClassSingletonInstance
    private static class InnerClassSingletonInstance {
        private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
    }

    // 提供一个静态的共有方法,直接返回 InnerClassSingletonInstance.INSTANCE
    public static synchronized InnerClassSingleton getInstance() {
        return InnerClassSingletonInstance.INSTANCE;

    }
}
  • 枚举
    不仅能避免多线程同步问题,还能防止反序列化重新创建新的对象,推荐使用
public class EnumSingleton {
    enum EnumSingletonInstance {
    	// 属性
        INSTANCE;
        public void itGod() {
            System.out.println("ITGodRoad");
        }
    }
}
单例模式的扩展
  • 有限的多例模式
    生成有限个实例并保存在 ArrayList 中,需要时可随机获取
多例类Multiton -ArrayList<Multiton>:list -Multiton(n:int) +getRandomInstance() : Multiton 访问类Client #instance : Multiton
    static {
        for (int i = 0; i < n; i++) {
            list.add(new Multion(i));
        }
    }

    public static Multiton getRandomInstance() {
        int value = (int) (Math.random() * n);
        return list.get(value);
    }

 


3. 原型模式

原型(Prototype)用一个已经创建的实例作为原型,通过对其进行复制或克隆出多个和原型相同或相似的新对象。有些系统中,存在大量相同或相似对象的创建问题,如果用传统的构造函数来创建对象,会比较复杂且耗时耗资源,用原型模式生成对象就很高效,就像孙悟空拔下猴毛轻轻一吹就变出很多孙悟空一样简单。

原型模式优点和缺点
  • 优点
  1. Java 自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良。
  2. 可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
  • 缺点
  1. 需要为每一个类都配置一个 clone 方法
  2. clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则。
  3. 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
原型模式的应用场景
  1. 对象之间相同或相似,即只是个别的几个属性不同的时候。
  2. 创建对象成本较大,例如初始化时间长,占用CPU太多,或者占用网络资源太多等,需要优化资源。
  3. 创建一个对象需要繁琐的数据准备或访问权限等,需要提高性能或者提高安全性。
  4. 系统中大量使用该类对象,且各个调用者都需要给它的属性重新赋值。
原型模式的结构
1. 抽象原型类:规定了具体原型对象必须实现的接口。
2. 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
3. 访问类:使用具体原型类中的 clone() 方法来复制新的对象。
«Interface» 抽象原型Prototype +clone() : Prototype 具体原型Realizetype +clone() : Realizetype 访问类Client +main() : void Realization
原型模式的实现
由于 Java 提供了对象的 clone() 方法,所以用 Java 实现原型模式很简单。
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
//具体原型类
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));
    }
}
-----------------------------------------------------------------
・【运行结果】
	具体原型创建成功!
	具体原型复制成功!
	obj1==obj2?false

具体实例 → 点击跳转

原型模式的扩展
  • 原型管理器模式
    在原型模式的基础上增加了一个原型管理器 PrototypeManager 类。该类用 HashMap 保存多个复制的原型,Client 类可以通过管理器的 get(String id) 方法从中获取复制的原型。
«Interface» 抽象原型Prototype +clone() : Prototype 具体原型1Realizetype1 +clone() : Prototype 具体原型2Realizetype2 +clone() : Prototype 访问类Client +main() : void 原型管理器PrototypeManager -HashMap<String ,Prototype> +put(String id,Prototype p) : void +get(Stirng id) : Prototype Realization Realization
package com.it.god.controller;

import java.util.HashMap;
import java.util.Scanner;

public class PrototypeTestController {

    public static void main(String[] args) {
        ProtoTypeManager pm = new PrototypeTestController().new ProtoTypeManager();
        Shape obj1 = pm.getShape("Circle");
        obj1.countArea();
        Shape obj2 = pm.getShape("Square");
        obj2.countArea();

    }

    class ProtoTypeManager {
        private HashMap<String, Shape> hp = new HashMap<String, Shape>();

        public ProtoTypeManager() {
            hp.put("Circle", new Circle());
            hp.put("Square", new Square());
        }

        public void addshape(String key, Shape obj) {
            hp.put(key, obj);
        }

        public Shape getShape(String key) {
            Shape temp = hp.get(key);
            return temp.clone();
        }
    }

    interface Shape extends Cloneable {
        public Shape clone();

        public void countArea();
    }

    class Circle implements Shape {
        @Override
        public Circle clone() {
            Circle circle = null;
            try {
                circle = (Circle) super.clone();
            } catch (CloneNotSupportedException e) {
                System.out.println("拷贝圆失败!");
            }
            return circle;
        }

        @Override
        public void countArea() {
            int r = 0;
            System.out.print("这是一个圆,请输入圆的半径:");
            @SuppressWarnings("resource")
            Scanner input = new Scanner(System.in);
            r = input.nextInt();
            System.out.println("该圆的面积=" + 3.1415 * r * r + "\n");
        }
    }

    class Square implements Shape {
        @Override
        public Square clone() {
            Square b = null;
            try {
                b = (Square) super.clone();
            } catch (CloneNotSupportedException e) {
                System.out.println("拷贝正方形失败!");
            }
            return b;
        }

        @Override
        public void countArea() {
            int a = 0;
            System.out.print("这是一个正方形,请输入它的边长:");
            @SuppressWarnings("resource")
            Scanner input = new Scanner(System.in);
            a = input.nextInt();
            System.out.println("该正方形的面积=" + a * a + "\n");
        }
    }

}
-----------------------------------------------------------------
・【运行结果】
	这是一个圆,请输入圆的半径:4
	该圆的面积=50.264
	
	这是一个正方形,请输入它的边长:6
	该正方形的面积=36

 


4. 工厂方法模式

工厂方法(FactoryMethod)定义一个用于创建产品的接口,由子类决定生产什么产品。按实际业务场景划分,工厂模式有 3 种不同的实现方式,分别是简单工厂模式、工厂方法模式和抽象工厂模式。我们把被创建的对象称为“产品”,把创建产品的对象称为“工厂”。

简单工厂模式(简述)

如果要创建的产品不多,只要一个工厂类就可以完成,这种模式叫“简单工厂模式”。简单工厂模式每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度,违背了“开闭原则”。

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;
        }
    }
}
工厂方法模式优点和缺点
  • 优点
  1. 用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
  2. 灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
  3. 典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。
  • 缺点
  1. 类的个数容易过多,增加复杂度
  2. 增加了系统的抽象性和理解难度
  3. 抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决。
工厂方法模式的应用场景
  1. 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
  2. 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
  3. 客户不关心创建产品的细节,只关心产品的品牌。
工厂方法模式的结构
由4个要素构成:
1. 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。
2. 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
3. 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
4. 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
«Interface» 抽象工厂AbstractFactory +newProduct() : Product 具体工厂1ConcreteFactory1 +newProduct() : Product 具体工厂2ConcreteFactory2 +newProduct() : Product «Interface» 抽象产品Product +show() : void 具体产品1ConcreteProduct1 +show() : void 具体产品2ConcreteProduct2 +show() : void 访问类Client Realization Realization Realization Realization Create Create
工厂方法模式的实现

当需要生成的产品不多且不会增加,一个具体工厂类就可以完成任务时,可删除抽象工厂类。这时工厂方法模式将退化到简单工厂模式

<?xml version="1.0" encoding="UTF-8"?>
<config>
	<className>ConcreteFactory1</className>
</config>
package FactoryMethod;

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());
        }
    }
}

// 抽象产品:提供了产品的接口
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();
    }
}
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;
        }
    }
}

 


5. 抽象工厂模式

抽象工厂(AbstractFactory)工厂方法模式只考虑生产同种类(等级)的产品,抽象工厂模式考虑的是多种类(等级)的产品,是工厂方法模式的升级版本,提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。如农场里既养动物又种植物,电器厂既生产电视机又生产洗衣机或空调,大学既有软件专业又有生物专业等。

抽象工厂模式优点和缺点
  • 优点
  1. 可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
  2. 当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品族。
  3. 抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。
  • 缺点
  1. 当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。
抽象工厂模式的应用场景
  1. 系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
  2. 系统一次只可能消费其中某一族产品,即同族的产品一起使用。例如有人只喜欢穿某一个品牌的衣服和鞋。
  3. 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。
抽象工厂模式的结构
抽象工厂模式同工厂方法模式一样由4个要素构成,但抽象工厂中方法个数不同,抽象产品的个数也不同。
1. 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
2. 具体工厂(ConcreteFactory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
3. 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
4. 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
«Interface» 抽象工厂AbstractFactory +newProduct1() : Product1 +newProduct2() : Product2 具体工厂1ConcreteFactory1 +newProduct1() : Product1 +newProduct2() : Product2 具体工厂2ConcreteFactory2 +newProduct1() : Product1 +newProduct2() : Product2 «Interface» 抽象产品1Product1 +show() : void 具体产品11ConcreteProduct11 +show() : void 具体产品12ConcreteProduct12 +show() : void «Interface» 抽象产品2Product2 +show() : void 具体产品21ConcreteProduct21 +show() : void 具体产品22ConcreteProduct22 +show() : void 访问类Client Realization Realization Realization Realization Realization Realization Create Create Create Create
抽象工厂模式的实现

抽象工厂模式的结构同工厂方法模式的结构相似,不同的是其产品的种类不止一个,所以创建产品的方法也不止一个。当系统中只存在一个等级结构的产品时,抽象工厂模式将退化到工厂方法模式。

package FactoryMethod;

public class AbstractFactoryTest {
    public static void main(String[] args) {
        try {
            Product1 a;
            AbstractFactory af;
            af = (AbstractFactory) ReadXML1.getObject();
            a = af.newProduct1();
            a.show();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }
}

// 抽象产品1:提供了产品族1的接口
interface Product1 {
    public void show();
}

// 抽象产品2:提供了产品族2的接口
interface Product2 {
    public void show();
}

// 具体产品11:实现抽象产品族11中的抽象方法
class ConcreteProduct11 implements Product1 {
    public void show() {
        System.out.println("具体产品11显示...");
    }
}

// 具体产品12:实现抽象产品族12中的抽象方法
class ConcreteProduct12 implements Product1 {
    public void show() {
        System.out.println("具体产品12显示...");
    }
}

// 具体产品21:实现抽象产品族21中的抽象方法
class ConcreteProduct21 implements Product2 {
    public void show() {
        System.out.println("具体产品21显示...");
    }
}

// 具体产品22:实现抽象产品族22中的抽象方法
class ConcreteProduct22 implements Product2 {
    public void show() {
        System.out.println("具体产品22显示...");
    }
}

// 抽象工厂:提供了工厂的生成方法
interface AbstractFactory {
    public Product1 newProduct1();
    public Product2 newProduct2();
}

// 具体工厂1:实现了工厂的生成方法
class ConcreteFactory1 implements AbstractFactory {
    public Product1 newProduct1() {
        System.out.println("具体工厂1生成-->具体产品族1...");
        return new ConcreteProduct1();
    }
    public Product2 newProduc2t() {
        System.out.println("具体工厂1生成-->具体产品族2...");
        return new ConcreteProduct2();
    }
}

// 具体工厂2:实现了工厂的生成方法
class ConcreteFactory2 implements AbstractFactory {
    public Product1 newProduct1() {
        System.out.println("具体工厂2生成-->具体产品族1...");
        return new ConcreteProduct1();
    }
    public Product2 newProduc2t() {
        System.out.println("具体工厂2生成-->具体产品族2...");
        return new ConcreteProduct2();
    }
}

 


6. 建造者(Builder)模式

建造者(Builder)将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。建造者模式注重零部件的组装过程,而工厂方法模式更注重零部件的创建过程,两者可以结合使用。

建造者模式优点和缺点
  • 优点
  1. 封装性好,构建和表示分离。
  2. 扩展性好,各个具体的建造者相互独立,有利于系统的解耦。
  3. 客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险。
  • 缺点
  1. 产品的组成部分必须相同,这限制了其使用范围。
  2. 如果产品的内部变化复杂,产品内部发生变化时,建造者也要同步修改,后期维护成本较大。
建造者模式的应用场景
  1. 相同的方法,不同的执行顺序,产生不同的结果。
  2. 多个部件或零件,都可以装配到一个对象中,但是产生的结果又不相同。
  3. 产品类非常复杂,或者产品类中不同的调用顺序产生不同的作用。
  4. 初始化一个对象特别复杂,参数多,而且很多参数都具有默认值。
建造者模式的结构
建造者模式由产品、抽象建造者、具体建造者、指挥者等 4 个要素构成:
1. 产品(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个零部件。
2. 抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。
3. 具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
4. 指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。
«Interface» 抽象建造者Builder #Product +buildpartA() : void +buildpartB() : void +buildpartC() : void +getResult() : Product 具体建造者1ConcreteBuilder1 +builepartA() : void +builepartB() : void +builepartC() : void 具体建造者2ConcreteBuilder2 +builepartA() : void +builepartB() : void +builepartC() : void 指挥者Director -Builder +Director(Builder builder) +construct() : Product 产品Product -partA -partB -partC +setPartA(Stirng partA) : void +setPartB(Stirng partB) : void +setPartC(Stirng partC) : void +show() : void 访问类Client Realization Realization
建造者模式的实现

package com.it.god.controller;

import java.util.ArrayList;
import java.util.List;

public class BuilderTestController {

    public static void main(String[] args) {
        MealBuilder mealBuilder = new MealBuilder();

        Meal vegMeal = mealBuilder.prepareVegMeal();
        System.out.println("Veg Meal");
        vegMeal.showItems();
        System.out.println("Total Cost: " + vegMeal.getCost());

        Meal nonVegMeal = mealBuilder.prepareNonVegMeal();
        System.out.println("\n\nNon-Veg Meal");
        nonVegMeal.showItems();
        System.out.println("Total Cost: " + nonVegMeal.getCost());

    }

}
-----------------------------------------------------------------
// 制定套餐
class MealBuilder {

    public Meal prepareVegMeal() {
        Meal meal = new Meal();
        meal.addItem(new VegBurger());
        meal.addItem(new Coke());
        return meal;
    }

    public Meal prepareNonVegMeal() {
        Meal meal = new Meal();
        meal.addItem(new ChickenBurger());
        meal.addItem(new Pepsi());
        return meal;
    }
}

// 创建套餐
class Meal {
    private List<Item> items = new ArrayList<Item>();

    public void addItem(Item item) {
        items.add(item);
    }

    public float getCost() {
        float cost = 0.0f;
        for (Item item : items) {
            cost += item.price();
        }
        return cost;
    }

    public void showItems() {
        for (Item item : items) {
            System.out.print("Item : " + item.name());
            System.out.print(", Packing : " + item.packing().pack());
            System.out.println(", Price : " + item.price());
        }
    }
}
-----------------------------------------------------------------
// 食物接口
interface Item {
    public String name();

    public Packing packing();

    public float price();
}

// 包装接口
interface Packing {
    public String pack();
}

// 纸盒包装
class Wrapper implements Packing {

    @Override
    public String pack() {
        return "Wrapper";
    }
}

// 纸杯包装
class Bottle implements Packing {

    @Override
    public String pack() {
        return "Bottle";
    }
}
-----------------------------------------------------------------
// 汉堡抽象建造者
abstract class Burger implements Item {

    @Override
    public Packing packing() {
        return new Wrapper();
    }

    @Override
    public abstract float price();
}

// 冷饮抽象建造者
abstract class ColdDrink implements Item {

    @Override
    public Packing packing() {
        return new Bottle();
    }

    @Override
    public abstract float price();
}

// 蔬菜汉堡
class VegBurger extends Burger {

    @Override
    public float price() {
        return 25.0f;
    }

    @Override
    public String name() {
        return "Veg Burger";
    }
}

// 炸鸡汉堡
class ChickenBurger extends Burger {

    @Override
    public float price() {
        return 50.5f;
    }

    @Override
    public String name() {
        return "Chicken Burger";
    }
}

// 可乐
class Coke extends ColdDrink {

    @Override
    public float price() {
        return 30.0f;
    }

    @Override
    public String name() {
        return "Coke";
    }
}

// 百事
class Pepsi extends ColdDrink {

    @Override
    public float price() {
        return 35.0f;
    }

    @Override
    public String name() {
        return "Pepsi";
    }
}
-----------------------------------------------------------------
・【运行结果】
	Veg Meal
	Item : Veg Burger, Packing : Wrapper, Price : 25.0
	Item : Coke, Packing : Bottle, Price : 30.0
	Total Cost: 55.0
	
	
	Non-Veg Meal
	Item : Chicken Burger, Packing : Wrapper, Price : 50.5
	Item : Pepsi, Packing : Bottle, Price : 35.0
	Total Cost: 85.5

 


【每日一面】

建造者模式和工厂模式的区别

建造者模式 创建复杂的对象,不仅要创建出对象,还要知道对象由哪些部件组成。根据建造过程中的不同组件或者相同组件的不同顺序,可以产生出一个新的对象,它可以产生一个非常灵活的架构,方便地扩展和维护系统。
工厂模式不关心构建过程,只注重创建对象,创建出来的对象都一样。
建造者模式关心细节,工厂模式关心整体

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值