设计模式二:建造者,原型,单例
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连接都有。至于类图,要把书上的理解透。那是最精炼的东西了