一、单例模式
二、工厂模式
三、抽象工厂模式
四、原型模式
五、建造者模式
注:学习视频:黑马程序员Java设计模式
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。
这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。
创建模式分为:
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
单例模式主要有以下角色:
- 单例类。只能创建一个实例的类
- 访问类。使用单单例类
单例模式设计模式分两种:
饿汉式:类加载就会导致该单例对象被创建。
懒汉式:类加载不会导致单例对象被创建,而是首次调用该对象时才会被创建
1. 饿汉式:方式1(静态变量方式):
静态成员变量方式可以实现,是因为静态变量只有在类加载的时候被初始化一次。
/**
* 饿汉式(静态成员变量)
*/
public class Singleton {
// 将构造器私有化
private Singleton(){}
// 创建静态成员变量
private static Singleton instance = new Singleton();
// 提供获取静态成员变量方法
public static Singleton getInstance(){
return instance;
}
}
说明:
该方式在成员位置声明
Singleton
类型的静态变量,并创建Singleton
类的对象instance
。instance
对象是随着类的加载而创建的。如果该对象足够大的话,而一直没有使用就会造成内存的浪费。
2. 饿汉式:方式2(静态代码块):
/**
* 饿汉式(静态代码块)
*/
public class Singleton {
// 私有化构造方法
private Singleton(){}
// 声明静态实例对象
private static Singleton instance;
static {
instance = new Singleton();
}
// 提供获取静态成员变量方法
public static Singleton getInstance(){
return instance;
}
}
说明:
该方式在成员位置声明
Singleton
类型的静态变量,而对象的创建是在静态代码块中,也是对着类的加载而创建。所以和饿汉式的方式1基本上一样,当然该方式也存在内存浪费问题。
1. 懒汉式-方式1(线程不安全)
/**
* 懒汉式(线程不完全)
*/
public class Singleton {
//私有化构造器
private Singleton(){}
// 声明静态变量
private static Singleton instance;
public static Singleton getInstance() {
if(instance == null){
// 线程1进入,又切换到了线程2 会导致两个线程进入,破坏了单例模式
instance = new Singleton();
}
return instance;
}
}
说明:
线程不完全问题,如果一个线程进入了判断是否为
null
,这个时候被切换到另一个进程,这样的话,有两个进入了判断,创建了两个实例,就不是单例模式了。
2.懒汉式-方式2(线程安全)
/**
* 懒汉式(线程不完全)
*/
public class Singleton {
//私有化构造器
private Singleton(){}
// 声明静态变量
private static Singleton instance;
public static synchronized Singleton getInstance() {
if(instance == null){
// 线程1进入,又切换到了线程2 会导致两个线程进入,破坏了单例模式
instance = new Singleton();
}
return instance;
}
}
说明:
线程安全的方式只是在线程不安全的方式基础上,在获取实例的方法上加上了
synchronized
关键字,保证了线程安全。
3.懒汉式-方式3(双重检查锁)
/**
* 懒汉式双重检查锁
*/
public class Singleton {
// 私有化构造方法
private Singleton(){}
// 声明静态变量
private static Singleton instance;
// 获取方法
public static Singleton getInstance() {
// 第一判断,如果instance不等于null,就不进入抢锁阶段,
// 这样的话可以节省抢锁的时间,提高性能
if(instance == null){
synchronized (Singleton.class){
// 抢到锁之后再进入判断是否为null 不是则直接返回instance
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
说明:
双重检查锁,实际上是通过线程安全锁的思想上来改进的,将抢锁时机改变,如果
instance
为null
,则不进入抢锁,直接返回,这样一来节省了抢锁的时间,所有性能也提高了。但是仍然存在一定的问题,在多线程的情况下,可能会出现空指针问题,出现问题的原因是JVM在实例化对象的时候会进行优化和指令重排序操作。
改进:
可以使用
volatile
关键字可以保证可见性和有序性
/**
* 懒汉式双重检查锁
*/
public class Singleton {
// 私有化构造方法
private Singleton(){}
// 声明静态变量
private static volatile Singleton instance;
// 获取方法
public static Singleton getInstance() {
// 第一判断,如果instance不等于null,就不进入抢锁阶段,
// 这样的话可以节省抢锁的时间,提高性能
if(instance == null){
synchronized (Singleton.class){
// 抢到锁之后再进入判断是否为null 不是则直接返回instance
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
4.懒汉式-方式4(静态内部类方式)
静态内部类单例模式中实例由内部类创建,由于
JVM
在加载外部类的过程
中, 是不会加载静态内部类
的, 只有内部类的属性/方法被调用时才会被加载, 并初始化其静态属性。静态属性由于被static
修饰,保证只被实例化一次,并且严格保证实例化顺序。
/**
* 懒汉式(静态内部类)
*/
public class Singleton {
// 私有构造方法
private Singleton(){}
// 定义静态内部
public static class SingletonInter{
// 声明静态变量 防止外面修改 instance 可以加上final
private static final Singleton INSTANCE = new Singleton();
}
// 对外提供静态方法获取该对象
public static Singleton getInstance(){
return SingletonInter.INSTANCE;
}
}
说明:
第一次加载的时候,由于
INSTANCE
是写在静态内部类里面的,所有不会加载该变量,这样就实现了饿汉式,因为SingletonInter
是静态内部类,在第一次调用的时候,对他进行加载,后面调用将不会再加载,所以是线程安全的。
枚举类实现单例模式是极力推荐的单例实现模式,因为枚举类型是线程安全的,并且只会装载一次,设计者充分的利用了枚举的这个特性来实现单例模式,枚举的写法非常简单,而且枚举类型是所用单例实现中唯一一种不会被破坏的单例实现模式。
/**
* 枚举方式
*/
public enum Singleton {
INSTANCE;
}
说明:
枚举方式属于饿汉式
破坏单例模式:使上面定义的单例类(Singleton)可以创建多个对象,枚举方式除外。
有两种方式,分别是序列化和反射。
采用静态内部类演示,其他单例模式实现方式也存在同样问题
/**
* 懒汉式(静态内部类)
必须实现序列化接口
*/
public class Singleton implements Serializable {
// 私有构造方法
private Singleton(){}
// 定义静态内部
public static class SingletonInter{
// 声明静态变量 防止外面修改 instance 可以加上final
private static final Singleton INSTANCE = new Singleton();
}
// 对外提供静态方法获取该对象
public static Singleton getInstance(){
return SingletonInter.INSTANCE;
}
}
实现破坏单例模式的方法,将获取的单例对象
instance
写入到文件中,再从文件中读取对象,连续读取两次获取两个instance
,这两个对象不是同一个对象,就破坏了单例模式。
public class Test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
writeObjectToFile();
readObjectFromFile();
readObjectFromFile();
/**
* 输入如下,不是同一个对象,已经破坏了单例模式
*com.zcc.singleton.dome5.Singleton@312b1dae
* com.zcc.singleton.dome5.Singleton@7530d0a
*/
}
/**
*将对象写入文件
* @throws IOException
*/
public static void writeObjectToFile() throws IOException {
Singleton instance = Singleton.getInstance();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\安逸i\\Desktop\\a.txt"));
oos.writeObject(instance);
}
/**
* 从文件中读取出对象
* @throws IOException
* @throws ClassNotFoundException
*/
public static void readObjectFromFile() throws IOException, ClassNotFoundException {
ObjectInputStream ooi = new ObjectInputStream(new FileInputStream("C:\\Users\\安逸i\\Desktop\\a.txt"));
Singleton instance = (Singleton) ooi.readObject();
System.out.println(instance);
}
}
在Singleton类中添加
readResolve()
方法,在底层中反序列化的时候会检查该对象是否有readResolve()
,如果定义了这个方法,就返回这个方法的值,如果没有定义,则返回新new出来的对象。其实在底层原理中反序列化的时候会检查该对象。
/**
* 懒汉式(静态内部类)
加上了 readResolve() 方法
*/
public class Singleton implements Serializable {
// 私有构造方法
private Singleton(){}
// 定义静态内部
public static class SingletonInter{
// 声明静态变量 防止外面修改 instance 可以加上final
private static final Singleton instance = new Singleton();
}
// 对外提供静态方法获取该对象
public static Singleton getInstance(){
return SingletonInter.instance;
}
// 添加 readResolve() 方法,解决序列化和反序列化问题
private Object readResolve(){
return SingletonInter.instance;
}
}
com.zcc.singleton.dome5.Singleton@3764951d
com.zcc.singleton.dome5.Singleton@3764951d
/**
* 懒汉式(静态内部类)
*/
public class Singleton {
// 私有构造方法
private Singleton(){}
// 定义静态内部
public static class SingletonInter{
// 声明静态变量 防止外面修改 instance 可以加上final
private static final Singleton instance = new Singleton();
}
// 对外提供静态方法获取该对象
public static Singleton getInstance(){
return SingletonInter.instance;
}
}
public class Test {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// 获取Singleton字节码对象
Class<Singleton> aClass = Singleton.class;
// 获取无参构造器
Constructor<Singleton> noArg = aClass.getDeclaredConstructor();
//由于无参构造器是私有的 所有要取消访问检查
noArg.setAccessible(true);
// 通过反射调用无参构造器,获得对象
Singleton instance = noArg.newInstance();
Singleton instance1 = noArg.newInstance();
// 判断是否为同一对象
System.out.println(instance == instance1); // 返回false 破坏了单例模式
}
}
思路: 既然是通过调用无参构造器来创建的对象,那就可以从无参构造器下手,判断是否已经存在对象,为了解决线程安全的问题,采用
synchronized
来实现线程安全
public class Singleton {
// 定义一个全局静态变量来判断是否已经有 Singleton对象
private static boolean flag = false;
// 私有构造方法
private Singleton(){
// 为了实现线程安全,给代码上锁
synchronized (Singleton.class){
// 如果已经有了 Singleton 类 则抛出异常
if(flag){
throw new RuntimeException("不能创建多个对象");
}
flag = true;
}
}
// 定义静态内部
public static class SingletonInter{
// 声明静态变量 防止外面修改 instance 可以加上final
private static final Singleton instance = new Singleton();
}
// 对外提供静态方法获取该对象
public static Singleton getInstance(){
return SingletonInter.instance;
}
}
Exception in thread “main” java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at com.zcc.singleton.dome6.Test.main(Test.java:20)
Caused by: java.lang.RuntimeException: 不能创建多个对象
at com.zcc.singleton.dome6.Singleton.(Singleton.java:16)
… 5 more
需求:设计一个咖啡店点餐系统。
设计一个咖啡类(Coffee),并定义其两个子类(美式咖啡【AmericanCoffee】和拿铁咖啡【LatteCoffee】);再设计一个咖啡店类(CoffeeStore),咖啡店具有点咖啡的功能。
三种工厂模式:
简单工厂模式(不属于GOF的23种经典设计模式)
工厂模式
抽象工厂模式
简单工厂不是一张设计模式,像是一种变成习惯
简单工厂包含如下角色:
工厂类代码:
public class SimpleFactory {
// 传入咖啡类型,创建对应的咖啡
// 具体工厂创建具体产品,返回抽象产品
public Coffee createCoffee(String typeName){
Coffee coffee = null;
if(typeName.equals("americano")){
coffee = new AmericanoCoffee();
}else if(typeName.equals("latte")){
coffee = new LatteCoffee();
}else {
throw new RuntimeException("没有你点的咖啡");
}
return coffee;
}
}
说明:
创建了工厂之后,对于咖啡店来说,就可以通过名字来直接调用工厂的方法来获得对象,将创建过程封装了起来,降低了耦合度,但是也伴随的新的耦合产生,对于咖啡店和工厂对象之间的耦合,工厂对象和商品对象的耦合。
封装了创建对象的过程,客户可以通过参数直接获得对象,这样的话,减少了客户代码修改的可能,不需要客户来直接创建对象。
还是违背了“开闭原则”,如果需要增加一个具体产品,则需要修改的工厂类,违背了开闭原则。
针对于简单工厂模式,使用工厂方法模式就可以完美解决,完成遵循开闭原则。
定义一个用于创建对象的接口,让子类决定实例化哪一个产品类都西昂,工厂方式使一个产品的实例化延迟到了子类。
工厂方法模式的主要角色:
代码如下:
public interface CoffeeFactory {
Coffee createCoffee();
}
public class LatteCoffeeFactory implements CoffeeFactory {
public Coffee createCoffee() {
return new LatteCoffee();
}
}
public class AmericanCoffeeFactory implements CoffeeFactory {
public Coffee createCoffee() {
return new AmericanCoffee();
}
}
public class CoffeeStore {
private CoffeeFactory factory;
public CoffeeStore(CoffeeFactory factory) {
this.factory = factory;
}
public Coffee orderCoffee() {
Coffee coffee = factory.createCoffee();
coffee.addMilk();
coffee.addsugar();
return coffee;
}
}
public class CoffeeStore {
AbstractCoffeeFactory abstractCoffeeFactory;
public CoffeeStore(AbstractCoffeeFactory coffeeFactory){
this.abstractCoffeeFactory = coffeeFactory;
}
public void orderCoffee(String typeName){
Coffee coffee = abstractCoffeeFactory.createCoffee();
coffee.drink();
coffee.addMilk();
coffee.addSugar();
}
}
说明:
可以看到,这样设计的话,如果需要增加一个产品类时,虽然使不会改变原来的代码,但是每增加一个产品,就要增加产品类,和对应的具体工厂类,如果类的数量比较大,就会造成类爆炸问题。
优点:
缺点:
工厂方法类所生产的都是同一级别/种类的产品,但是在实际生活中可能会出现需要生产同一品牌的产品,如果使用工厂方法,就需要创建多个抽象工厂,具体工厂,我们为什么不将同一品牌的产品聚合起来,放在一个工厂里面,这样可以满足一个工厂生产出多级别的产品。
1、抽象工厂模式是一种提供一个创建一组相关或相互依赖对象接口,且访问类无需指定所要的产品的具体类就能访问得到同族的不等级的产品的模式结构
2、抽象工厂实际上工厂方法模式的升级版,工厂方法模式只生产一个等级的产品,而抽象工厂可生产多个级别的产品。
抽象工厂模式主要角色:
现在咖啡店,不仅要生产咖啡,还要生产甜点,假设生产的甜点是提拉米苏、莫斯蛋糕,提拉秘书和属于同一族,莫斯蛋糕和美式咖啡属于同一族,我们就可以将同一族的两个产品放到同一工厂里面。
/**
* 包含创建两个等级的产品的方法
*/
public interface DessertFactory {
Coffee createCoffee();
Dessert createDessert();
}
//美式甜点工厂
public class AmericanDessertFactory implements DessertFactory {
public Coffee createCoffee() {
return new AmericanCoffee();
}
public Dessert createDessert() {
return new MatchaMousse();
}
}
//意大利风味甜点工厂
public class ItalyDessertFactory implements DessertFactory {
public Coffee createCoffee() {
return new LatteCoffee();
}
public Dessert createDessert() {
return new Tiramisu();
}
}
说明:
如果需要加一个产品族的话,只需要添加一个具体工厂实现抽象工厂即可,不需要修改其他类。
优点:
当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
缺点:
当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。
可以通过工厂模式+配置文件的方式解除工厂对象和产品对象的耦合。在工厂类中加载配置文件中的全类名,并创建对象进行存储,客户端如果需要对象,直接进行获取即可
// 抽象产品类
public abstract class Coffee {
public void drink(){};
}
// 两个具体产品类
public class AmericanCoffee extends Coffee{
public void drink(){
System.out.println("美式咖啡");
}
}
public class LatteCoffee extends Coffee {
public void drink(){
System.out.println("拿铁咖啡");
}
}
american=com.itheima.pattern.factory.config_factory.AmericanCoffee
latte=com.itheima.pattern.factory.config_factory.LatteCoffee
public class CoffeeFactory {
// 创建集合保存对象
private static Map<String, Coffee> map = new HashMap<>();
// 加载类的时候创建对象放入到集合中
static {
Properties properties = new Properties();
try {
// 加载配置文件
properties.load(new FileInputStream("bean.properties"));
Set<Object> objects = properties.keySet();
for (Object obj :objects) {
// 获取全类名
String className = (String) properties.get(obj);
Class<?> aClass = Class.forName(className);
Coffee coffee = (Coffee) aClass.newInstance();
map.put((String) obj,coffee);
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
// 对外提供获取coffee的方法
public Coffee getInstance(String typeName){
return map.get(typeName);
}
}
public class Client {
public static void main(String[] args) {
CoffeeFactory coffeeFactory = new CoffeeFactory();
Coffee latte = coffeeFactory.getInstance("latte");
latte.drink(); // 拿铁咖啡
}
}
public class Demo {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("令狐冲");
list.add("风清扬");
list.add("任我行");
//获取迭代器对象
Iterator<String> it = list.iterator();
//使用迭代器遍历
while(it.hasNext()) {
String ele = it.next();
System.out.println(ele);
}
}
}
对上面的代码大家应该很熟,使用迭代器遍历集合,获取集合中的元素。而单列集合获取迭代器的方法就使用到了工厂方法模式。我们看通过类图看看结构:
Collection接口是抽象工厂类,ArrayList是具体的工厂类;Iterator接口是抽象商品类,ArrayList类中的Iter内部类是具体的商品类。在具体的工厂类中iterator()方法创建具体的商品类的对象。
另:
1,DateForamt类中的getInstance()方法使用的是工厂模式;
2,Calendar类中的getInstance()方法使用的是工厂模式;
用一个已经创建的实例作为原型,来创建一个与原型对象相同的新对象
原型模式包含如下角色:
clone()
方法clone()
方法。clone()
方法复制出新的对象原型模式分为浅克隆和深克隆
**浅克隆:**创建一个新的对象,新对象的属性和原型对象的完全相同,对于非基本属性类型,仍指向原来所指向的内存地址。
**深克隆:**创建一个新对象,新对象属性中引用的其他对象会被克隆,不再指向原来的地址。
/**
* 具体原型类
*/
public class RealizeType implements Cloneable{
// 属性
private String name;
// 用来判断克隆是不是通过构造方法来创建的
public RealizeType() {
System.out.println("调用了无参构造方法");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 重写clone方法 返回当前具体原型类,实现克隆
@Override
public RealizeType clone() throws CloneNotSupportedException {
return (RealizeType) super.clone();
}
// 实现类方法
public void study(){
System.out.println(name + "正在学习");
}
}
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
RealizeType realizeType = new RealizeType();
realizeType.setName("张三");
RealizeType clone = realizeType.clone();
System.out.println(clone == realizeType);
clone.setName("李四");
realizeType.study();
clone.study();
}
}
// 输出如下
// 可以看出克隆不是通过调用构造方法创建
调用了无参构造方法
false // 可见不是同一对象
张三正在学习
李四正在学习
一个公司中多个员工,采用其中一个员工作为原型类,通过原型模式创建多个员工
代码如下:
/**
* 员工类
*/
public class Employee implements Cloneable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 重写clone方法
@Override
public Employee clone() throws CloneNotSupportedException {
return (Employee) super.clone();
}
// 编写类方法
public void work(){
System.out.println(name + "正在上班...");
}
}
// 测试类
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Employee employee = new Employee();
employee.setName("张三");
Employee clone = employee.clone();
clone.setName("李四");
System.out.println(clone == employee);
clone.work();
employee.work();
}
}
// 输出如下
false // 两者是不同的对象
李四正在上班...
张三正在上班...
- 对象的创建非常复杂,可以使用原型模式快捷的创建对象。
- 性能和安全要求比较高。
深克隆是为了解决克隆的原型对象中存在引用类型的数据,克隆后新对象引用类型属性仍然指向原来的地址,深拷贝可以它是新的地址
/**
* 学生类
*/
public class Student {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void study(){
System.out.println(name + "正在上课!!!");
}
}
/**
* 班级类
*/
public class Classes implements Cloneable{
// 引用了student类
private Student stu;
public Student getStu() {
return stu;
}
public void setStu(Student stu) {
this.stu = stu;
}
// 重写clone方法
@Override
public Classes clone() throws CloneNotSupportedException {
return (Classes) super.clone();
}
}
/**
* 测试类
*/
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
// 原型对象
Classes classes = new Classes();
Student student = new Student();
classes.setStu(student);
classes.getStu().setName("张三");
// 克隆对象
Classes clone = classes.clone();
clone.getStu().setName("李四");
classes.getStu().study();
classes.getStu().study();
}
}
// 测试结果如下
李四正在上课!!!
李四正在上课!!! // 说明两个student是同一个对象
采用对象流来实现深克隆,只需修改测试类,和将原型对象序列化,引用类型数据序列化。
/**
* 测试类
*/
public class Client {
public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
// 获取原型对象
Classes classes = new Classes();
classes.setStu(new Student());
classes.getStu().setName("张三");
// 获取输出流对象,将原型对象写入文件中
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:\\a.txt"));
oos.writeObject(classes);
oos.close();
// 获取输入对象流,读取创建新对象
ObjectInputStream ooi = new ObjectInputStream(new FileInputStream("d:\\a.txt"));
Classes clone = (Classes) ooi.readObject();
clone.getStu().setName("李四");
classes.getStu().study();
clone.getStu().study();
}
}
// 输出结果如下
张三正在上课!!!
李四正在上课!!! // 可见两个student没有指向同一个地址
将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式包含如下角色:
类图图下:
假设要创建一辆汽车,是一个非常复杂的对象,我们只写其中的两个部件,轮子(Wheel) 和 椅子(Chair)两个部件, 采用创建者方式。
/**
* 汽车类
*/
public class Car {
// 椅子部件,这里使用字符串作为部件,真正使用是传入对象作为部件
private String chair;
private String wheel;
public String getChair() {
return chair;
}
public void setChair(String chair) {
this.chair = chair;
}
public String getWheel() {
return wheel;
}
public void setWheel(String wheel) {
this.wheel = wheel;
}
@Override
public String toString() {
return "Car{" +
"chair='" + chair + '\'' +
", wheel='" + wheel + '\'' +
'}';
}
}
/**
* 抽象创建者
*/
public abstract class Builder {
// 将car对象创建在抽象创建者中,实现类就可以对这个类进行创建部件
Car car = new Car();
abstract void setWheel();
abstract void setChair();
abstract Car createCar();
}
/**
* 奔驰创造者类
*/
public class BCCarBuilder extends Builder{
@Override
void setWheel() {
car.setWheel("奔驰轮胎");
}
@Override
void setChair() {
car.setChair("奔驰座椅");
}
@Override
Car createCar() {
return car;
}
}
/**
* 宝马车创建者
*/
public class BmCarBuilder extends Builder{
@Override
void setWheel() {
car.setWheel("米其林轮胎");
}
@Override
void setChair() {
car.setChair("真皮座椅");
}
@Override
Car createCar() {
return car;
}
}
/**
* 指挥者类
*/
public class Director {
private Builder builder;
// 初始话创建者
public Director(Builder builder){
this.builder = builder;
}
// 构建方法,装配部件
public Car construct(){
builder.setChair();
builder.setWheel();
Car car = builder.createCar();
return car;
}
}
/**
* 测试类
*/
public class Client {
public static void main(String[] args) {
// 奔驰车创建
BCCarBuilder bcCarBuilder = new BCCarBuilder();
Director director = new Director(bcCarBuilder);
Car car = director.construct();
// 宝马车创建
BmCarBuilder bmCarBuilder = new BmCarBuilder();
Director director1 = new Director(bmCarBuilder);
Car car1 = director1.construct();
System.out.println(car);
System.out.println(car1);
}
}
// 测试结果
Car{chair='奔驰座椅', wheel='奔驰轮胎'}
Car{chair='真皮座椅', wheel='米其林轮胎'}
注意:
可以看出,指挥者类对于产品的创建有着至关重要的作用,Builder只是提供了部件的创建,而真正决定怎么创建的,需要由指挥者类(Director)来决定,当部件比较少的时候,可以将指挥者类合并到抽象创建者类里面
补充:
上诉代码中,对于Car这产品也可以抽取出一个抽象类,让具体类来实现的。可以将耦合度。
/**
* 抽象创建者
*/
public abstract class Builder {
// 将car对象创建在抽象创建者中,实现类就可以对这个类进行创建部件
Car car = new Car();
abstract void setWheel();
abstract void setChair();
abstract Car createCar();
public Car construct(){
this.setWheel();
this.setChair();
Car car = this.createCar();
return car;
}
}
优点:
缺点:
建造者(Builder)模式创建的是复杂对象,其产品的各个部分经常面临着剧烈的变化,但将它们组合在一起的算法却相对稳定,所以它通常在以下场合使用。
建造者模式还可以用来处理一个类的构造器传入很多参数时,可读性差,很容易记不住字段的顺序造成字段赋值出错的问题,可以采用的建造者模式来重构
重构前的代码:
public class Phone {
private String cpu;
private String screen;
private String memory;
private String mainboard;
public Phone(String cpu, String screen, String memory, String mainboard) {
this.cpu = cpu;
this.screen = screen;
this.memory = memory;
this.mainboard = mainboard;
}
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public String getScreen() {
return screen;
}
public void setScreen(String screen) {
this.screen = screen;
}
public String getMemory() {
return memory;
}
public void setMemory(String memory) {
this.memory = memory;
}
public String getMainboard() {
return mainboard;
}
public void setMainboard(String mainboard) {
this.mainboard = mainboard;
}
@Override
public String toString() {
return "Phone{" +
"cpu='" + cpu + '\'' +
", screen='" + screen + '\'' +
", memory='" + memory + '\'' +
", mainboard='" + mainboard + '\'' +
'}';
}
}
// 测试类
public class Client {
public static void main(String[] args) {
//构建Phone对象,这个地方很容易赋值错误
Phone phone = new Phone("intel","三星屏幕","金士顿","华硕");
System.out.println(phone);
}
}
// 测试结果
Phone{cpu='intel', screen='三星屏幕', memory='金士顿', mainboard='华硕'}
采用创建者模式改进:
/**
* 静态内部创建者类改进后
*/
public class Phone {
private String cpu;
private String screen;
private String memory;
private String mainboard;
// 将构造方法私有化,采用创建者实现构建
private Phone(Builder builder){
this.cpu = builder.cpu;
this.screen = builder.screen;
this.memory = builder.memory;
this.mainboard = builder.mainboard;
}
// 创建一个静态内部创建者类
public static class Builder{
private String cpu;
private String screen;
private String memory;
private String mainboard;
// 分别创建属性
public Builder setCpu(String val){
cpu =val;
// return this 将 builder 返回,后面可以连写
return this;
}
public Builder setScreen(String val){
screen = val;
return this;
}
public Builder setMemory(String val){
memory = val;
return this;
}
public Builder setMainboard(String val){
mainboard = val;
return this;
}
// 外部类的构造方式已经私有了,所以通过内部类方法返回一个Phone对象
public Phone build(){
// 内部类可以直接调用外部类的私有构造器
// 再将内部类这个对象传给私有构造器,让他来进行构造
return new Phone(this);
}
}
@Override
public String toString() {
return "Builder{" +
"cpu='" + cpu + '\'' +
", screen='" + screen + '\'' +
", memory='" + memory + '\'' +
", mainboard='" + mainboard + '\'' +
'}';
}
}
// 测试类
public class Client {
public static void main(String[] args) {
// 通过改进后,我们就可以通过调用静态内部类的方法,来实现构造器方法
// 由于内部类每设置一个属性就返回 Builder 所有可以连接赋值
// 并且还可以选择性的构造,不易造成错误
Phone phone = new Phone.Builder()
.setCpu("Intel")
.setScreen("三星")
.setMemory("金士顿")
.setMainboard("华硕")
.build();
System.out.println(phone);
}
}
// 测试结果
Builder{cpu='Intel', screen='三星', memory='金士顿', mainboard='华硕'}
工厂方法模式注重的是整体对象的创建方式;而建造者模式注重的是部件构建的过程,意在通过一步一步地精确构造创建出一个复杂的对象。
我们举个简单例子来说明两者的差异,如要制造一个超人,如果使用工厂方法模式,直接产生出来的就是一个力大无穷、能够飞翔、内裤外穿的超人;而如果使用建造者模式,则需要组装手、头、脚、躯干等部分,然后再把内裤外穿,于是一个超人就诞生了。
抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式则是不需要关心构建过程,只关心什么产品由什么工厂生产即可。
建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。
如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车。