设计模式二:建造者,原型,单例

https://github.com/tori22/DesignPattern

最近在看大话设计模式,顺带敲了书上的代码。一定要多敲!!!!

之前,讲了三个工厂方法,我们来简单的复习一下

1.抽象工厂: Factory

提供一个创建一系列或相关依赖对象的接口,而无需指定他们具体的类。针对多级结构.

抽象工厂模式除了具有工厂方法模式的优点外,最主要的优点就是可以在类的内部对产品族进行约束。

产品族的扩展将是一件十分费力的事情,假如产品族中需要增加一个新的产品,

则几乎所有的工厂类都需要进行修改。

适用场景

当需要创建的对象是一系列相互关联或相互依赖的产品族时,便可以使用抽象工厂模式。

总结:生产多系列产品!!!

2.工厂方法

生产单个产品,大工厂下有几个小工厂,每个工厂生产具体的东西

定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂模式使一个类的

实例化延迟到其子类。针对单一结构系统.

适用场景:

作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式

假如调用者自己组装产品需要增加依赖关系时,可以考虑使用工厂模式。

当需要系统有比较好的扩展性时,可以考虑工厂模式

3.简单工厂

就只有一个大工厂,不符合开放--封闭准则

 

开始今天的学习了。

一.建造者

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

使用建造者模式的好处

1.使用建造者模式可以使客户端不必知道产品内部组成的细节。

2.具体的建造者类之间是相互独立的,对系统的扩展非常有利。

3.由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。

使用建造者模式的场合:

1.创建一些复杂的对象时,这些对象的内部组成构件间的建造顺序是稳定的,但是

对象的内部组成构件面临着复杂的变化。

2.要创建的复杂对象的算法,独立于该对象的组成部分,也独立于组成部分的装配方法时。

package BuilderPattern;

public abstract class Builder {

    public abstract void buildPartA();

    public abstract void buildPartB();

    public abstract Product getResult();
}

package BuilderPattern;

public class Client {

    public static void main(String[] args) {
        Director director = new Director();
        Builder builder1 = new ConcrateBuilder1();
        Builder builder2 = new ConcrateBuilder2();

        director.Construct(builder1);
        Product p1 = builder1.getResult();
        p1.show();

        director.Construct(builder2);
        Product p2 = builder2.getResult();
        p2.show();
    }
}
package BuilderPattern;

public class ConcrateBuilder1 extends Builder {

    private Product product = new Product();
    @Override
    public void buildPartA() {
        product.add("部件A");
    }

    @Override
    public void buildPartB() {
        product.add("部件B");
    }

    @Override
    public Product getResult() {
        return product;
    }
}
package BuilderPattern;

public class ConcrateBuilder2 extends Builder {

    private Product product = new Product();
    @Override
    public void buildPartA() {
        product.add("部件x");
    }

    @Override
    public void buildPartB() {
        product.add("部件y");
    }

    @Override
    public Product getResult() {
        return product;
    }
}package BuilderPattern;

public class Director {

    public void Construct(Builder builder) {
        builder.buildPartA();
        builder.buildPartB();
    }
}
package BuilderPattern;

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

public class Product {

    private List<String> parts = new ArrayList<>();

    public void add(String part) {
        parts.add(part);
    }

    public void show() {
        System.out.println("产品 创建-----");
        for (String part: parts) {
            System.out.println(part);
        }
    }
}

输出为

产品 创建-----
部件A
部件B
产品 创建-----
部件x
部件y

 

二.原型

package Prototype;



public class Client {

    public static void main(String[] args) {
        ConcreatePrototype1 p1 = new ConcreatePrototype1("I");
        ConcreatePrototype1 c1 = (ConcreatePrototype1)p1.Clone();

        String str = String.format("id:%s",c1.getId());
        System.out.println(str);

        ConcreatePrototype2 p2 = new ConcreatePrototype2("Y");
        ConcreatePrototype2 c2 = (ConcreatePrototype2)p2.Clone();

        String str2 = String.format("id:%s",c2.getId());
        System.out.println(str2);

    }
}
package Prototype;

public class ConcreatePrototype1 extends Prototype implements Cloneable{

    public ConcreatePrototype1(String id) {
        super(id);
    }
    @Override
    public Prototype Clone() {
        Prototype prototype = null;

        try {
            prototype = (Prototype) clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return prototype;
    }
}
package Prototype;

public class ConcreatePrototype2 extends Prototype implements Cloneable{

    public ConcreatePrototype2(String id) {
        super(id);
    }
    @Override
    public Prototype Clone() {
        Prototype prototype = null;

        try {
            prototype = (Prototype) clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return prototype;
    }
}
package Prototype;

public abstract class Prototype {
    private String id;

    public Prototype(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }

    public abstract Prototype Clone();

}

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

使用原型模式创建对象比直接new一个对象在性能上要好的多,因为Object类的clone方法是一个本地方法,

它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。

使用原型模式的另一个好处是简化对象的创建,使得创建对象就像我们在编辑文档时的复制粘贴一样简单。

因为以上优点,所以在需要重复地创建相似对象时可以考虑使用原型模式。比如需要在一个

循环体内创建对象,假如对象创建过程比较复杂或者循环次数很多的话,使用原型模式不但可以简化创建过程,

而且可以使系统的整体性能提高很多。

 

三.单例

package SingletonPattern;

public class Client {
    public static void main(String[] args) {
        Singleton singleton1 = Singleton.getInstance();
        Singleton singleton2 = Singleton.getInstance();

        if(singleton1 == singleton2) {
            System.out.print("两个对象是相同的实例");
        }
    }

}
package SingletonPattern;

public class Singleton {

    private static Singleton mInstance;
    private static final Object mLock = new Object();

    private Singleton() {

    }

    public static Singleton getInstance() {
        if (mInstance == null) {
            synchronized (mLock) {
                if (mInstance == null) {
                    mInstance = new Singleton();
                }
            }
        }
        return mInstance;
    }
}

保证一个类仅有一个实例,并提供一个访问它的全局访问点.

让类自身负责保存它的唯一实例,这个类可以保证没有其他实例可以被创建,

并且我还提供了一个访问该实例的方法。这样就使得对唯一的实例可以严格

地控制客户怎样以及何时访问它。

 

饿汉式

package SingletonPattern;

/*
*****************
* 饿汉式,就是屌丝,穷,担心饿死。类加载就给准备好
* ****************
 */
public class SingletonPattern1 {
    /*
    ********************************
    * 有时会加final修饰符,添加final修饰符之后,指向的引用不能再做更改
    * 这是final的用法:final成员变量表示常量,只能被赋值一次,赋值后不能再改变。
    *
    * 这句话得这么理解
    * 对于一个final变量
    * 如果是基本数据类型的变量,则其数值一旦初始化之后就不能再更改
    * 如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象
    * ******************************
     */
    private static final SingletonPattern1 singletonInstance = new SingletonPattern1();

    private SingletonPattern1() {

    }

    public static SingletonPattern1 getSingletonInstance() {
        return singletonInstance;
    }
}

懒汉式

package SingletonPattern;

/*
**********
*  饱汉式(懒汉式)----就是有钱,豪,用的时候再new(线程不安全)
*  *******
 */
public class SingletonPattern2 {
    //这个就不能加final,因为要在其他地方给他再次赋值呢。
    //加了final,那就默认一直是null啦,而且还不能再次给此属性赋值。
    //此属性是静态,那么就是共享数据,多线程并发操作共享数据是有可能的。那么就会出现下面的线程不安全现象。
    private static SingletonPattern2 singletonPattern2;

    private SingletonPattern2() {}

    public static SingletonPattern2 getSingletonPattern2() {
        if (singletonPattern2 == null) {

            //在这个地方,多线程的时候,
            //可能A线程挂起,此属性还是null,那么B线程可能也判断条件OK也进来啦。
            //然后A线程可以执行的时候就会new个对象,线程B也会new个对象。
            //就不能保证内存的唯一性。也就是线程不安全
            singletonPattern2 = new SingletonPattern2();
        }
        return singletonPattern2;
    }

    ///**
    // * 为了应对上述的不安全,可以简单的如下操作给方法添加[synchronized],使之成为同步函数。
    // * 但是:
    // * 在很多线程的情况下,就每个线程访问都得判断锁,效率就是问题。所以,才有后面的[双重锁形式]
    // */
    //public static synchronized SingletonPattern2 getSingletonInstance() {
    //    if (singletonInstance == null) {
    //        singletonInstance = new SingletonPattern2();
    //    }
    //    return singletonInstance;
    //}
}

双重锁(线程不安全)

package SingletonPattern;

public class SingletonPattern3 {
/*
**********************
 双重锁形式
 * 这个模式将同步内容下放到if内部,提高了执行的效率,不必每次获取对象时都进行同步,
 * 只有第一次才同步,创建了以后就没必要了。避免土豪模式下创建单例,可能存在的线程不安全问题。
 * *********************
 */
    private static SingletonPattern3 singletonInstance;

    private SingletonPattern3() {
    }

    /**
     * 静态方法同步的时候,使用的锁,就不能是this,而是类.class
     */
    public static SingletonPattern3 getSingletonInstance() {
        if (singletonInstance == null) {
            //这个地方可能有多个线程,在这排队,ABCD..。
            synchronized (SingletonPattern3.class) {
                if (singletonInstance == null) {
                    //假设第一次A线程走到这,然后,呈挂起状态。这个时候,单例对象还未创建;
                    // 假设此时,B线程也来了判断单例对象==null成立,但是,因为A线程已经给里层的if判断上锁,所以,B只能在外等着。
                    //假设A线程被唤醒,那么,单例就会下面语句赋值,单例对象就创建啦。然后释放锁。B就可以进来啦。
                    //B线程进来之后,先判断单例对象是否为null,发现已经不是null啦,那么就不需要创建啦。
                    //CD线程同样,
                    //再往后面来的,第一个if就进不来啦,那就不会判断锁了。
                    singletonInstance = new SingletonPattern3();
                }
            }
        }
        return singletonInstance;
    }
}

 

今天就到这里了,学习设计模式,一定要多敲,代码在GitHub连接都有。至于类图,要把书上的理解透。那是最精炼的东西了

 

posted on 2018-04-12 22:00  小嘤嘤  阅读(223)  评论(0编辑  收藏  举报

导航