23种设计模式

整体分为3大类:
建立型模式 (5种):工厂方法、抽象工厂、单例、建造者、原型
结构型模式(7种):适配器、装饰器、代理、外观、桥接、组合、享元
行为型模式(11种):策略、模板方法、观察者、迭代子、责任链、命令、备忘录、状态、访问者、中介者、解释器
其它(2种):并发型、线程池java

设计模式的6大原则

  • 开闭原则(总原则):对扩展开放,对修改关闭,须要使用接口和抽象类
  1. 单一职责:每一个类应该实现单一的职责
  2. 里氏替换:基类能够出现的地方,子类必定能够出现。相似于向上转型,子类对父类的方法尽可能不要重写和重载,会破坏父类定义好的与外界交互规范。是对开闭原则的补充,实现开闭原则的关键是抽象化,里氏替换原则是对实现抽象化规范
  3. 依赖倒转:面向接口编程,依赖于抽象,不依赖于具体。代码中:不与具体类交互,与接口交互。开闭原则的基础。
  4. 接口隔离:接口中的方法在子类中必定能用到,不然拆分红多个接口
  5. 迪米特法则(最少知道):对外暴露越少越好
  6. 合成复用:尽可能使用合成/聚合的方式,而不是使用继承

建立型模式(5种):工厂方法、抽象工厂、单例、建造者、原型。

1、工厂方法模式

clipboard.png
建立1个接口、2个实现类编程

public interface Sender {
    void send();
}
public class MailSender implements Sender {

    @Override
    public void send() {
        System.out.println("this is mailsender!");
    }
}
public class SmsSender implements Sender {

    @Override
    public void send() {
        System.out.println("this is smssender!");
    }
}

建立1个工厂接口、2个工厂实现类segmentfault

public interface Provider {
    Sender produce();
}
public class SendMailFactory implements Provider {
    @Override
    public Sender produce() {
        return new MailSender();
    }
}
public class SendSmsFactory implements Provider {
    @Override
    public Sender produce() {
        return new SmsSender();
    }
}

测试类设计模式

public class Test {
    public static void main(String[] args) {
        Provider provider = new SendMailFactory();
        Sender sender = provider.produce();
        sender.send();
    }
}

clipboard.png

若是想增长一个功能,则只需作一个实现类,实现 Sender 接口,同时作一个工厂类,实现 Provider 接口,就 OK 了,无需去改动现成的代码。这样作,拓展性较好!多线程

2、抽象工厂模式

工厂方法模式和抽象工厂模式的区别以下:
工厂方法模式:并发

  • 1个抽象产品类能够派生出多个具体产品类。
  • 1个抽象工厂类能够派生出多个具体工厂类。1个具体工厂类只能建立1个具体产品类的实例。

抽象工厂模式:ide

  • 多个抽象产品类,1个抽象产品类能够派生出多个具体产品类。
  • 1个抽象工厂类能够派生出多个具体工厂类。1个具体工厂类能够建立多个具体产品类的实例,也就是建立的是一个产品线下的多个产品。

对于 java 来讲,你能见到的大部分抽象工厂模式都是这样的:
---它的里面是一堆工厂方法,每一个工厂方法返回某种类型的对象。
好比说工厂能够生产鼠标和键盘。那么抽象工厂的实现类(它的某个具体子类)的对象均可以生产鼠标和键盘,但可能工厂 A 生产的是罗技的键盘和鼠标,工厂 B 是微软的。
用了工厂方法模式,你替换生成键盘的工厂方法,就能够把键盘从罗技换到微软。可是用了抽象工厂模式,你只要换家工厂,就能够同时替换鼠标和键盘一套。若是你要的产品有几十个,固然用抽象工厂模式一次替换所有最方便(这个工厂会替你用相应的工厂方法)因此说抽象工厂就像工厂,而工厂方法则像是工厂的一种产品生产线性能

3、单例模式(Singleton)

单例模式能保证在一个JVM中该对象只有一个实例存在。好处:
一、 避免频繁建立对象,节省系统开销,减轻 GC 压力。
二、在系统中某些对象只能有一个(好比一个军队出现了多个司令员同时指挥,确定会乱成一团)测试

  1. 简单的单例类(单线程):
    只能在单线程中用,不能用于多线程。优化

    public class Singleton {
        /* 持有私有静态实例,防止被引用,此处赋值为 null,目的是实现延迟加载 */
        private static Singleton instance = null;
    
        /* 私有构造方法,防止被实例化 */
        private Singleton() {
        }
    
        /* 静态工程方法,建立实例 */
        public static Singleton getInstance() {
            if (instance == null){
                instance = new Singleton();
            }
            return instance;
        }
    
        /* 若是该对象被用于序列化,能够保证对象在序列化先后保持一致 */
        public Object readResolve(){
            return instance;
        }
    }

    同步代码块:

    /* 静态工程方法,建立实例 */
    public static Singleton getInstance() {
        if (instance == null){
            synchronized (instance){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

    在 Java 指令中建立对象和赋值操做是分开进行的,也就是说 instance = new Singleton();语句是分两步执行的,可能会先为Singleton实例分配空间,再赋值给instance,最后初始化Singleton实例。
    A、B 两个线程为例:

    1. A、B 线程同时进入了第一个 if 判断
    2. A首先进入 synchronized 块,因为 instance 为 null,因此它执行 instance = new Singleton();
    3. 因为 JVM 内部的优化机制,JVM 先画出了一些分配给 Singleton 实例的空白内存,并赋值给 instance 成员(注意此时 JVM 没有开始初始化这个实例),而后 A 离开了 synchronized块。
    4. B进入 synchronized 块,因为 instance 此时不是 null,所以它立刻离开了 synchronized 块并将结果返回给调用该方法的程序。
    5. 此时 B 线程打算使用 Singleton 实例,却发现它没有被初始化,因而错误发生了。
      上面这些话能够理解为A线程从同步代码块出来后,JVM没有初始化Singleton实例,B线程调用instance时发现Singleton没有初始化。
  2. 多线程单例
    使用内部类来维护单例的实现,JVM 内部的机制可以保证当一个类被加载的时候,这个类的加载过程是线程互斥的。这样当咱们第一次调用 getInstance 的时候,JVM 可以帮咱们保证 instance 只被建立一次,而且会保证把赋值给 instance 的内存初始化完毕,这样咱们就不用担忧上面的问题。同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题。

    public class Singleton {
        /* 私有构造方法,防止被实例化 */
        private Singleton() {
        }    
        /* 此处使用一个内部类来维护单例 */
        private static class SingletonFactory{
            private static Singleton instance = new Singleton();
        }    
        /* 获取实例 */
        public static Singleton getInstance(){
            return SingletonFactory.instance;
        } 
        /* 若是该对象被用于序列化,能够保证对象在序列化先后保持一致 */
        public Object readResolve(){
            return getInstance();
        }
    }
  3. 将建立和赋值分开,单独为建立类加静态同步方法
    由于咱们只须要在建立类的时候进行同步,因此只要将建立和getInstance()分开,单独为建立加 synchronized 关键字,也是能够的

    public class SingletonTest {
        private static SingletonTest instance = null;
        public SingletonTest() {
        }
        private static synchronized void syncInit(){
            if(instance == null){
                instance = new SingletonTest();
            }
        }
        public static SingletonTest getInstance() {
            if (instance == null){
                syncInit();
            }
            return instance;
        }
    }

    补充:用 采用" 影子实例"的办法为单例对象的属性同步更新

    public class SingletonTest {
        private static SingletonTest instance = null;
        private Vector properties = null;
        public Vector getProperties() {
            return properties;
        }
        public SingletonTest() {
        }
        private static synchronized void syncInit(){
            if(instance == null){
                instance = new SingletonTest();
            }
        }
        public static SingletonTest getInstance() {
            if (instance == null){
                syncInit();
            }
            return instance;
        }
        public void updateProperties(){
            SingletonTest shadow = new SingletonTest();
            properties = shadow.getProperties();
        }
    }

    类和静态方法与静态类区别:

    1. 静态类不能实现接口。(从类的角度说是能够的,可是那样就破坏了静态了。由于接口中不容许有 static 修饰的方法,因此即便实现了也是非静态的)
    2. 单例能够被延迟初始化,静态类通常在第一次加载是初始化。之因此延迟加载,是由于有些类比较庞大,因此延迟加载有助于提高性能。
    3. 单例类能够被继承,他的方法能够被覆写。可是静态类内部方法都是 static,没法被覆写。

4、建造者模式(Builder)

5、原型模式(Prototype)

将一个对象做为原型,对其进行复制、克隆后产生一个和原对象相似的新对象
浅复制:将一个对象复制后,基本数据类型的变量都会从新建立,而引用类型,指向的仍是原对象所指向的。
深复制:将一个对象复制后,不管是基本数据类型还有引用类型,都是从新建立的。
一个原型类,只须要实现 Cloneable 接口,覆写 clone 方法,此处 clone 方法能够改为任意的名称,由于 Cloneable 接口是个空接口,你能够任意定义实现类的方法名,如 cloneA或者cloneB,由于此处的重点是super.clone()这句话,super.clone()调用的是Object的clone()方法,而在 Object 类中,clone()是 native 的。这里写一个深浅复制的例子

public class Prototype implements Cloneable, Serializable {

    private static final long serialVersionUID = 1L;
    private String string;
    private SerializableObject obj;

   /*浅复制*/
    public Object clone() throws CloneNotSupportedException{
        Prototype proto = (Prototype)super.clone();
        return proto;
    }

    /*深复制*/
    public Object deepClone() throws IOException, ClassNotFoundException {
        /* 写入当前对象的二进制流 */
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        /* 读出二进制流产生的新对象 */
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();

    }

    public String getString() {
        return string;
    }

    public void setString(String string) {
        this.string = string;
    }

    public SerializableObject getObj() {
        return obj;
    }

    public void setObj(SerializableObject obj) {
        this.obj = obj;
    }
}

class SerializableObject implements Serializable{
    private static final long serialVersionUID = 1L;
}
相关文章
相关标签/搜索