设计模式学习总结(2)单例模式、建造者模式、原型模式

单例模式(Singleton Pattern)

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

单例模式有以下三点注意:

  • 1、单例类只能有一个实例。
  • 2、单例类必须自己创建自己的唯一实例。
  • 3、单例类必须给所有其他对象提供这一实例。

单例的好处:

  1. 某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
  2. 省去了new操作符,降低了系统内存的使用频率,减轻GC压力。
  3. 有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(比如一个军队出现了多个司令员同时指挥,肯定会乱成一团),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。

缺点:

没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

注意:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。

单例模式的实现方式一共有6种,一般情况下,不建议使用第 1 种和第 2 种懒汉方式,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式(建议使用这个方法)。如果涉及到反序列化创建对象时,可以尝试使用第 6 种枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式。

1、懒汉式,线程不安全

是否 Lazy 初始化:

是否多线程安全:

总结:最大的问题就是不支持多线程,

/**
 * @ClassName: Singleton1
 * @Description: 懒汉式,线程不安全
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 11:18
 */
public class Singleton1 {
    private static Singleton1 instance;
    private Singleton1(){}
    public static Singleton1 getInstance(){
        if(instance == null){
            instance = new Singleton1();
        }
        return instance;
    }
    public void showMessage(){
        System.out.println("hello singleton1");
    }
}
/**
 * @ClassName: SingletonPatternDemo
 * @Description: 测试demo
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 11:19
 */
public class SingletonPatternDemo {
    public static void main(String[] args){
        Singleton1 singleton1 = Singleton1.getInstance();
        singleton1.showMessage();
    }
}

2、懒汉式,线程安全

是否 Lazy 初始化:

扫描二维码关注公众号,回复: 3345688 查看本文章

是否多线程安全:

总结:能进行多线程,但是加锁会导致效率很低,优点就是避免了内存的浪费。这个还是有一个问题的,就是看到别的大神指出,如果A线程进入getInstance函数创建instance实例,不过并没有进行赋值操作,但是B线程这时也要使用这个类创建的唯一实例,但是他发现instance已经建立,但是访问却什么也没有,因而出现问题。

/**
 * @ClassName: Singleton2
 * @Description: 懒汉式,线程安全
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 11:28
 */
public class Singleton2 {
    private static Singleton2 instance;
    private Singleton2(){}
    public static synchronized Singleton2 getInstance(){
        if(instance == null){
            instance = new Singleton2();
        }
        return instance;
    }
    public void showMessage(){
        System.out.println("hello singleton2");
    }
}

3、饿汉式

是否 Lazy 初始化:

是否多线程安全:

总结:它基于 classloader 机制避免了多线程的同步问题,但是缺点就是造成了内存的浪费,没有达到lazy loading的效果。我认为相当于把instance的声明和获取分开了。

/**
 * @ClassName: Singleton3
 * @Description: 饿汉式
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 11:37
 */
public class Singleton3 {
    private static Singleton3 instance = new Singleton3();
    private Singleton3(){}
    public static Singleton3 getInstance(){
        return instance;
    }
    public void showMessage(){
        System.out.println("hello singleton3");
    }
}

4、双检锁/双重校验锁(DCL,即 double-checked locking)

是否 Lazy 初始化:

是否多线程安全:

总结:这种方式采用双锁机制,安全且在多线程情况下能保持高性能。(虽然代码也模仿的写出来了,但是感觉还是有点不明白啊)

/**
 * @ClassName: Singleton4
 * @Description: java类作用描述
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 11:47
 */
public class Singleton4 {
    private volatile static Singleton4 instance;
    private Singleton4(){}
    public static Singleton4 getInstance(){
        if(instance == null){
            synchronized (Singleton4.class){
                if(instance == null){
                    instance = new Singleton4();
                }
            }
        }
        return instance;
    }
    public void showMessage(){
        System.out.println("hello singleton4");
    }
}

5、登记式/静态内部类

是否 Lazy 初始化:

是否多线程安全:

总结:也是利用了classloader机制保证初始化instance只有一个线程,采用了类里面再单独使用一个类去初始化的方法,可以有效地去避免内存的浪费,还可以保证效率,是一个常用的办法

/**
 * @ClassName: Singleton5
 * @Description: 登记式/静态内部类
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 11:54
 */
public class Singleton5 {
    private static class SingletonHolder{
        private static final Singleton5 instance = new Singleton5();
    }
    private Singleton5(){}
    public static Singleton5 getInstance(){
        return  SingletonHolder.instance;
    }
    public void showMessage(){
        System.out.println("hello singleton5");
    }
}

6、枚举

是否 Lazy 初始化:

是否多线程安全:

总结:这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
不能通过 reflection attack 来调用私有构造方法。(这个方法还需要再研究啊。。。)

/**
 * @ClassName: Singleton6
 * @Description: 枚举
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 12:06
 */
public enum Singleton6 {
    INSTANCE;
    public void showMessage(){
        System.out.println("hello singleton6");
    }
}
        //方法6:枚举
        Singleton6 singleton6 = Singleton6.INSTANCE;
        singleton6.showMessage();

建造者模式(Builder Pattern)

一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。

主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。

优点: 1、建造者独立,易扩展。 2、便于控制细节风险。

缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。

/**
 * @ClassName: Packing
 * @Description: 包装类接口
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 12:30
 */
public interface Packing {
    public String pack();
}
/**
 * @ClassName: Bottle
 * @Description: 包装类接口实体类
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 12:32
 */
public class Bottle implements Packing{
    @Override
    public String pack() {
        return "bottle";
    }
}
/**
 * @ClassName: Wrapper
 * @Description: 纸质包装实体类
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 12:31
 */
public class Wrapper implements Packing{
    @Override
    public String pack(){
        return "wrapper";
    }
}
/**
 * @ClassName: Item
 * @Description: 食物条目接口
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 12:29
 */
public interface Item {
    public String name();
    public Packing packing();
    public float price();
}
/**
 * @ClassName: Burger
 * @Description: java类作用描述
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 12:33
 */
public abstract class Burger implements Item{
    @Override
    public Packing packing() {
        return new Wrapper();
    }

//    @Override
//    public abstract float price();
}
/**
 * @ClassName: ColdDrink
 * @Description: java类作用描述
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 13:11
 */
public abstract class ColdDrink implements Item{
    @Override
    public Packing packing() {
        return new Bottle();
    }
}
/**
 * @ClassName: VerBurger
 * @Description: java类作用描述
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 13:12
 */
public class VerBurger extends Burger{
    @Override
    public float price() {
        return 25.0f;
    }

    @Override
    public String name() {
        return "VerBurger";
    }
}
/**
 * @ClassName: ChickenBurger
 * @Description: java类作用描述
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 13:14
 */
public class ChickenBurger extends Burger{
    @Override
    public float price() {
        return 50.5f;
    }

    @Override
    public String name() {
        return "chickenbuger";
    }
}
/**
 * @ClassName: Coke
 * @Description: java类作用描述
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 13:15
 */
public class Coke extends ColdDrink{
    @Override
    public float price() {
        return 10.0f;
    }

    @Override
    public String name() {
        return "coke";
    }
}
/**
 * @ClassName: Pepsi
 * @Description: java类作用描述
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 13:16
 */
public class Pepsi extends ColdDrink{
    @Override
    public float price() {
        return 9.0f;
    }

    @Override
    public String name() {
        return "pepsi";
    }
}
import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName: Meal
 * @Description: 创建一个 Meal 类,带有上面定义的 Item 对象
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 13:21
 */
public class Meal {
    public 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.println("Item:"+item.name());
            System.out.println("Packing:"+item.packing().pack());
            System.out.println("price:"+item.price());
        }
    }
}
/**
 * @ClassName: MealBuilder
 * @Description: 创建一个 MealBuilder 类,实际的 builder 类负责创建 Meal 对象。
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 13:27
 */
public class MealBuilder {
    public Meal prepareVegMeal(){
        Meal meal = new Meal();
        meal.addItem(new VerBurger());
        meal.addItem(new Coke());
        return meal;
    }

    public Meal perpareChickenMeal(){
        Meal meal = new Meal();
        meal.addItem(new ChickenBurger());
        meal.addItem(new Pepsi());
        return meal;
    }
}
/**
 * @ClassName: BuilderPatternDemo
 * @Description: BuiderPatternDemo 使用 MealBuider 来演示建造者模式(Builder Pattern)。
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 13:31
 */
public class BuilderPatternDemo {
    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 chickMeal = mealBuilder.perpareChickenMeal();
        System.out.println("chick meal");
        chickMeal.showItems();
        System.out.println("total cost:" + chickMeal.getCost());
    }
}

原型模式(Prototype Pattern)

这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。

注意事项:与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流。

/**
 * @ClassName: Shape
 * @Description: 创建一个实现了 Clonable 接口的抽象类。
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 14:16
 */
public abstract class Shape implements Cloneable{
    private String id;
    protected String type;

    abstract void draw();

    public String getType() {
        return type;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Object clone(){
        Object clone = null;
        try{
            clone = super.clone();
        }catch (CloneNotSupportedException e){
            e.printStackTrace();
        }
        return clone;
    }
}
/**
 * @ClassName: Square
 * @Description: java类作用描述
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 14:23
 */
public class Square extends Shape{
    public Square(){
        type = "square";
    }

    public void draw(){
        System.out.println("draw square");
    }
}
/**
 * @ClassName: Rectangle
 * @Description: 拓展抽象类的实体类
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 14:21
 */
public class Rectangle extends Shape{
    public Rectangle(){
        type = "Rectangle";
    }

    public void draw(){
        System.out.println("draw rectangle");
    }
}
import java.util.Hashtable;

/**
 * @ClassName: ShapeCache
 * @Description: 创建一个类,从数据库获取实体类,并把它们存储在一个 Hashtable 中。
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 14:55
 */
public class ShapeCache {
    private static Hashtable<String,Shape> shapeMap = new Hashtable<String,Shape>();

    public static Shape getShape(String shapeId){
        Shape cachedShape = shapeMap.get(shapeId);
        return (Shape)cachedShape.clone();
    }

    public static void loadCache(){
        Square square = new Square();
        square.setId("1");
        shapeMap.put(square.getId(),square);

        Rectangle rectangle = new Rectangle();
        rectangle.setId("2");
        shapeMap.put(rectangle.getId(),rectangle);
    }
}
/**
 * @ClassName: PrototypePatternDemo
 * @Description: PrototypePatternDemo 使用 ShapeCache 类来获取存储在 Hashtable 中的形状的克隆。
 * @Author: xinyuan
 * @CreateDate: 2018/9/24 15:03
 */
public class PrototypePatternDemo {
    public static void main(String[] args){
        ShapeCache.loadCache();

        Shape clonedShape = (Shape) ShapeCache.getShape("1");
        System.out.println("Shape: " + clonedShape.getType());

        Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
        System.out.println("Shape: " + clonedShape2.getType());
    }
}

猜你喜欢

转载自blog.csdn.net/anyue0205/article/details/82827899
今日推荐