设计模式 — 创建型模式

AquariusYuxin 2019-06-27

简介

创建型模式,就是创建对象的模式,抽象了实例化的过程。它帮助一个系统独立于如何创建、组合和表示它的那些对象。关注的是对象的创建,创建型模式将创建对象的过程进行了抽象,也可以理解为将创建对象的过程进行了封装,作为客户程序仅仅需要去使用对象,而不再关心创建对象过程中的逻辑

单例模式

定义:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
类图:

设计模式 — 创建型模式

主要有三个部分:

私有的构造方法
指向自己实例的私有静态引用
以自己实例为返回值的静态的公有的方法

特点:*
优点:

在内存中只有一个对象,节省内存空间。
避免频繁的创建销毁对象,可以提高性能。
避免对共享资源的多重占用。
可以全局访问。

适用场景:

需要频繁实例化然后销毁的对象。
创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
有状态的工具类对象。
频繁访问数据库或文件的对象等等。

注意事项:

只能使用单例类提供的方法得到单例对象,不要使用反射,否则将会实例化一个新对象。
不要做断开单例类对象与类中静态引用的危险操作。
多线程使用单例使用共享资源时,注意线程安全问题。

饿汉式:

/**
 * 一:饿汉式(类加载的时候就创建了实例)
 * 优点:实现了线程安全,编写简单;执行效率高
 * 缺点:初始化就创建了实例,对加载速度和内存有消耗,,所以这种方法要求单例对象初始化速度快且占用内存小
 */
// 1、成员变量静态化
private static Singleton3 instance = new Singleton3();
//2、构造函数私有化,防止外部实例化
private Singleton3(){}
//3、提供公共静态创建实例方法
public static Singleton3 getInstance(){
    return instance;
}

 /**
 * 二:枚举类型:根据枚举类型的特点,实现单例模式所需要的创建单例、线程安全、简洁的需求
 * 优点:实现比较简洁,保证了单实例,线程安全
 * 缺点:使用枚举类型,不是很熟悉
 */

/**
 * 枚举类型的特点:
 * 1、枚举类型 = 不可被继承的类(final):
 *      枚举本质上是通过普通类实现的,只是编译器为我们进行了特殊处理
 *      每个枚举类型都继承自java.lang.Enum,并自动添加了values(),valueOf()
 *      枚举类的实例 = 常量
 * 2、每个枚举元素 = 类静态常量 = 1个实例
 *      枚举元素,都是通过静态代码来进行初始化,即在类加载期间进行初始化,保证了实例只被创建一次,线程安全
 *      获取枚举元素 = 获取实例
 * 3、构造方法 访问权限 默认=私有(private):他的构造器是私有的,底层没有可供调用的无参数的构造器;防止了其他人创建实例
 * 4、每一个枚举类型&枚举变量在JVM中都是唯一的:
 *      即java在序列化和反序列化美剧时做了特殊的规定:枚举的writeObject(),readObject(),readObjectNoData()等方法是被禁用的,因此不存在序列化接口之后调用readObject会破环单例的问题
 *      保证了枚举元素的不可变行,即不能通过克隆、序列化&反序列化来复制枚举,即保证了1个枚举常量=1个实例 即单例
 */


public enum Singleton {
    INSTANCE;
    public void whateverMethod() {
    }
}

懒汉式:

//程序中创建类只有两种方式:1创建类的一个对象,用该对象去调用类中的方法;2使用类名直接调用类中方法,类名.方法名();单例模式防止外部new对象,只能使用类方法,且是静态的。

/**
 * 一:懒汉式(需要的时候创建实例),线程不安全。当多个线程同时访问时,不能正常工作。
 * 优点:按需加载
 * 缺点:线程不安全。多个线程同时访问时,会创建多个实例。
 */
// 1、成员变量静态化
private static Singleton1 instance;
//2、构造函数私有化,防止外部实例化
private Singleton1(){}
//3、提供公共静态创建实例方法
public static Singleton1 getInstance() {
    if (instance == null) {
        instance = new Singleton1();
    }
    return instance;
}
/**
 * 二:懒汉式:线程安全,加同步锁,
 * 优点:实现了线程安全
 * 缺点:每次访问都要进行线程同步(调用synchronized锁),造成过多的同步开销(加锁=耗时、耗能)
 */

// 1、成员变量静态化
private static Singleton1 instance;
//2、构造函数私有化,防止外部实例化
private Singleton1(){}
//3、提供公共静态创建实例方法
public static synchronized Singleton1 getInstance() {
    if (instance == null) {
        instance = new Singleton1();
    }
    return instance;
}

// 1、成员变量静态化
private static Singleton1 instance;
//2、构造函数私有化,防止外部实例化
private Singleton1(){}
//3、提供公共静态创建实例方法
public static Singleton1 getInstance() {
    synchronized(Singleton.class){
        if (instance == null) {
            instance = new Singleton1();
        }
    }
    return instance;
}


/**
 *三:懒汉式改进—双重校验锁;在同步锁的基础上,添加一层if判断,若单例已经创建,则不需要再执行加锁操作就可以获取实例,从而提高了性能。
 *优点:避免了每次调用都要调用synchronized锁,同时双重校验又保证了线程安全,不会重复创建实例。
 * 缺点:较为复杂,容易出错
 */

// 1、成员变量静态化
private static Singleton1 instance = null;
//2、构造函数私有化,防止外部实例化
private Singleton1(){}
//3、提供公共静态创建实例方法

public static Singleton1 getInstance() {
    if (instance == null) {
        synchronized (Singleton1.class) {
            if (instance == null) {
                instance = new Singleton1();
            }
        }
    }
    return instance;
}

/**
 * 四:静态内部类。在静态内部类中创建单例,在加载内部类时才会创建单例
 * 优点:根据静态内部类的特性,实现了线程安全,按需加载,同时比较简洁
 */
// 1、创建静态内部类
private static class Singleton2{
    // 静态类里面创建单例
    private static Singleton1 instance = new Singleton1();
}

//2、构造函数私有化,防止外部实例化
private Singleton1(){}

//3、提供公共静态创建实例方法
public static Singleton1 getInstance() {
    //调用静态类的实例方法
    return Singleton2.instance;
}
/**
 * 调用过程说明
 * 1、外部调用类的getInstance()方法
 * 2、getInstance()方法自动调用内部类的方法,实现初始化
 * 3、而该类在装载 & 被初始化时,会初始化他的静态域,从而创建单例
 * 4、由于是静态域,因此JVM只会加载一遍,java虚拟机保证了线程安全
 * 5、最终实现只创建一个实例。
 */

简单工厂模式

定义:又静态工厂模式,可以根据参数的不同返回不同类的实例。
类图:

设计模式 — 创建型模式

由此可以看出:简单工厂模式由三部分组成:具体工厂、具体产品和抽象产品

工厂类(Creator)角色:担任这个角色的是简单工厂模式的核心,含有与应用紧密相关的商业逻辑。工厂类在客户端的直接调用下创建产品对象,它往往由一个具体Java类实现。

抽象产品(AbstractProduct)角色:担任这个角色的类是由简单工厂模式所创建的对象的父类,或它们共同拥有的接口。抽象产品角色可以用一个Java接口或者Java抽象类实现。

具体产品(ConcreteProduct)角色:简单工厂模式所创建的任何对象都是这个角色的实例,具体产品角色由一个具体Java类实现。

总结:

简单工厂只有一个工厂类,通过switch语句来区分创建哪个产品。
工厂方法模式是扩展了简单工厂模式,区分为抽象工厂和具体工厂实现。

工厂方法模式

定义:定义一个创建对象的工厂接口,让子类决定实例化哪一个类,将实际的创建工作推迟到子类当中。

类图:

设计模式 — 创建型模式

工厂方法模式包含

抽象产品:一般是产品接口或者抽象产品类。主要目的是定义产品的规范,所有的产品实现都必须遵循产品接口定义的规范。产品接口是调用者最为关心的,产品接口定义的优劣直接决定了调用者代码的稳定性。
具体产品:实现产品接口的具体类,决定了产品在客户端中的具体行为。
抽象工厂:一般是工厂接口或者抽象工厂类。是工厂方法模式的核心,与调用者直接交互用来提供产品。
具体工厂:在编程中,工厂实现决定如何实例化产品,是实现扩展的途径,需要有多少种产品,就需要有多少个具体的工厂实现。

优缺点:

优点:(1)封装了创建产品的方法,用户无须关心具体实现;(2)添加新产品时,只需要添加具体产品类和具体工厂类(实现或继承抽象产品、工厂类);(3)将创建工厂方法推迟到子类当中,实现了多态。工厂方法模式也叫多态工厂模式,之所以是多态,因为所有具体工厂类都具有同一父类。
缺点:(1)在添加新产品时,需要添加具体产品类、具体工厂类。当存在较多产品时,导致产品、产品工厂类增多,一定程度上增加了系统的复杂度,更多的类需要编译和运行,给系统带来了一些额外的开销。(2) 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。

代码举例

//1、抽象产品类
public interface Moveable {
    public void run();
}
//2、具体产品实现类
public class Car implements Moveable {
    @Override
    public void run() {
        System.out.println("小汽车!");
    }
}
public class Plane implements Moveable {
    @Override
    public void run() {
        System.out.println("大飞机!");
    }
}
//3、抽象工厂类
public abstract  class Factory {
    public abstract Moveable create();
}
//4、具体工厂实现类
public class CarFactory extends Factory {
    @Override
   public Moveable create() {
        return new Car();
    }
}
public class PlaneFactory extends Factory {
    @Override
    public Moveable create() {
        return new Plane();
    }
}
//测试类
public class FactoryTest {
    @Test
    public  void FactoryTest(){
        Factory car = new CarFactory();
        Moveable m = car.create();
//        Factory plane = new PlaneFactory();
//        Moveable m= plane.create();
        m.run();
    }
}

抽象工厂模式

定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

类图:

设计模式 — 创建型模式

可以看到,抽象工厂模式也是包含抽象产品、具体产品、抽象工厂、具体工厂。
但是抽象工厂中有产品等级结构和产品族的概念。

产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。
产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。

和工厂方法模式的区别:
工厂方法模式提供的是一个产品等级结构的实现,抽象工厂模式提供多个不同的产品等级结构的实现。

优缺点:

优点:可以在类的内部对产品族进行约束
缺点:产品族扩展比较麻烦。当增加新的产品时,所有的工厂类都要添加其相关代码。

代码举例:

//1、抽象产品类
public abstract class Vehicle{
    public abstract void run();
}

public abstract class Food {
    public abstract void printName();
}

public abstract class Weapon {
    public abstract void shoot();
}
//2、具体实现类
//第一个产品族
public class Car extends Vehicle {
    @Override
    public void run() {
        System.out.println("小汽车!");
    }
}
public class Apple extends Food{
    @Override
    public void printName() {
        System.out.println("红苹果");
    }
}
public class AK47 extends Weapon {
    @Override
    public void shoot() {
        System.out.println("大火球,huhuhu。。。。。。");
    }
}
//第2个产品族
public class Plane extends Vehicle {
    @Override
    public void run() {
        System.out.println("大灰机");
    }
}
public class Pizza  extends Food{
    @Override
    public void printName() {
        System.out.println("披萨");
    }
}
public class _98K extends Weapon{
    @Override
    public void shoot() {
        System.out.println("砰砰砰。。。。。。");
    }
}
//3、抽象工厂类
public  abstract class AbstractFactory {

    public abstract Vehicle createVehicle();
    public  abstract  Weapon createWeapon();
    public abstract Food createFood();
}
//4、具体工厂实现类
public class DefaultFactory extends AbstractFactory{

    @Override
    public Vehicle createVehicle() {
        return new Car();
    }

    @Override
    public Weapon createWeapon() {
        return new AK47();
    }

    @Override
    public Food createFood() {
        return new Apple();
    }
}

public class SuperFactory extends AbstractFactory{

    @Override
    public Vehicle createVehicle() {
        return new Plane();
    }

    @Override
    public Weapon createWeapon() {
        return new _98K();
    }

    @Override
    public Food createFood() {
        return new Pizza();
    }
}
//5、测试类
public class AbstractFactoryTest {
    @Test
    public void abstractFactoryTest(){
//        AbstractFactory a = new DefaultFactory();
        AbstractFactory a = new SuperFactory();
        Vehicle v= a.createVehicle();
        v.run();
        Weapon w = a.createWeapon();
        w.shoot();
        Food f= a.createFood();
        f.printName();
    }

}

总结:

建造者模式

定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

类图:

设计模式 — 创建型模式

建造者模式包含四个要素:

产品类:一般是一个较为复杂的对象,也就是说创建对象的过程比较复杂,一般会有比较多的代码量。在本类图中,产品类是一个具体的类,而非抽象类。实际编程中,产品类可以是由一个抽象类与它的不同实现组成,也可以是由多个抽象类与他们的实现组成。
抽象建造者:引入抽象建造者的目的,是为了将建造的具体过程交与它的子类来实现。这样更容易扩展。一般至少会有两个抽象方法,一个用来建造产品,一个是用来返回产品。
建造者:实现抽象类的所有未实现的方法,具体来说一般是两项任务:组建产品;返回组建好的产品。
导演类:负责调用适当的建造者来组建产品,导演类一般不与产品类发生依赖关系,与导演类直接交互的是建造者类。一般来说,导演类被用来封装程序中易变的部分。

优缺点:
优点:

将具体的业务逻辑实现封装在了导演类中,无须知道具体产品类的结构;
当添加新的产品时,只需要实现一个新的建造者类,并添加相应的导演类实现创建具体的产品。

代码实现:

class Product {
        private String name;
        private String type;
        public void showProduct(){
            System.out.println("名称:"+name);
            System.out.println("型号:"+type);
        }
        public void setName(String name) {
            this.name = name;
        }
        public void setType(String type) {
            this.type = type;
        }
    }

    abstract class Builder {
        public abstract void setPart(String arg1, String arg2);
        public abstract Product getProduct();
    }
    class ConcreteBuilder extends Builder {
        private Product product = new Product();

        public Product getProduct() {
            return product;
        }

        public void setPart(String arg1, String arg2) {
            product.setName(arg1);
            product.setType(arg2);
        }
    }

    public class Director {
        private Builder builder = new ConcreteBuilder();
        public Product getAProduct(){
            builder.setPart("宝马汽车","X7");
            return builder.getProduct();
        }
        public Product getBProduct(){
            builder.setPart("奥迪汽车","Q5");
            return builder.getProduct();
        }
    }
    public class Client {
        public static void main(String[] args){
            Director director = new Director();
            Product product1 = director.getAProduct();
            product1.showProduct();

            Product product2 = director.getBProduct();
            product2.showProduct();
        }
    }

与工厂模式的区别:
从结构上看,建造者模式只比工厂模式多了一个“导演类”的角色,如果没有这个导演类,就类似简单工厂模式了。
工厂模式是将对象的创建过程封装在工厂类中,由工厂类向客户端提供最终的产品。
建造者模式中,建造类一般只提供产品类中各个组件的建造,而将具体的建造过程交由导演类,由导演类实现具体的产品。
所以建造者模式一般用来创建更复杂的对象。

原型模式

待续。

参考资料

http://design-patterns.readth...
https://www.w3cschool.cn/java...

相关推荐