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


    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连接都有。至于类图,要把书上的理解透。那是最精炼的东西了

  • 相关阅读:
    Discuz! 的编码规范
    Golang 并发编程指南
    Hyrum's Law
    从数组中将变量导入到当前的符号表
    map[interface {}]interface {} yaml文件解码
    迪基福勒检验
    约定式路由
    use of internal package github.com/gokratos/kratos/v2/internal/httputil not allowed
    See https://v8.dev/blog/mathrandom for details.
    Cast a value as a certain type
  • 原文地址:https://www.cnblogs.com/xiaoyingying/p/8810716.html
Copyright © 2020-2023  润新知