浅析23种设计模式(1/3)- 构建型模式

发布于:2022-12-20 ⋅ 阅读:(530) ⋅ 点赞:(0)

构建型模式

工厂模式

1. 首先要明确,传统创建对象的方式是new一个对象,硬编码不利于程序的松耦合。工厂模式则提供了一种将对象创建过程封装起来的思想,是一种用于封装对象的设计模式。

2. 主要分为三类:简单工厂模式,工厂方法模式,抽象工厂模式

简单工厂模式:最为实用,大量创建某对象可以使用,但是可维护性,可扩展性很差

实现过程:

  • 创建披萨抽象类,封装基本的方法,若某个方法细节不同子类有区别则以抽象方式声明

    public abstract class Pizza {
        String name;
        //准备的原材料,不同披萨不一样,所以作为抽象类
        public abstract void prepare();
    ​
        public void bake(){
            System.out.println(name + "烤");
        }
    ​
        public void cut(){
            System.out.println(name + "切");
        }
    ​
        public void box(){
            System.out.println(name + "打包");
        }
    ​
        public void setName(String name) {
            this.name = name;
        }
    }
  • 创建其具体实现子类,满足不同需求

    public class ChessPizza extends Pizza{
        @Override
        public void prepare() {
            System.out.println("准备奶酪披萨原料");
        }
    }
    public class BeefPizza extends Pizza{
        @Override
        public void prepare() {
            System.out.println("准备牛肉披萨原料");
        }
    }
  • 创建工厂类,创建流程可以以静态方式实现,这样调用时无需实例化

    public class SinpleFactory {
        public static Pizza createPizza(String type){
            Pizza pizza = null;
            switch (type){
                case "beef":
                    pizza = new BeefPizza();
                    pizza.setName("BeefPizza");
                    break;
                case "chess":
                    pizza = new ChessPizza();
                    pizza.setName("ChessPizza");
                    break;
            }
            pizza.prepare();
            pizza.bake();
            pizza.cut();
            pizza.box();
            return pizza;
        }
    }
  • 最后,用户操作类,只需让用户指定需要得到的披萨种类,而无需知道制作细节

    public class OrderPizza {
    ​
        public Pizza getOrderPizza(String type){
            Pizza pizza = SinpleFactory.createPizza(type);
            return pizza;
        }
    }

工厂方法模式:就是将简单工厂模式的实例化过程延迟到子类进行,,这样,随着生产的产品越来越多时,工厂类不会变成超级类,当需要某个新产品时,只需要增加子类工厂即可,用户调用时同样只需要通过对应子类工厂调用即可,对应工厂已经将产品的创建过程进行了完美地封装。

抽象工厂模式:对工厂方法模式进行进一步的优化,可以提取出公共的工厂接口,这样在创建时指定了具体的工厂类后,在使用时就无需再关心是哪个工厂类,只需要将此工厂当作抽象的 接口使用即可,也正因如此,工厂的替换也将变得十分容易,整个底层逻辑和java的多态特性有着密切联系。

单例模式

1. 有效避免对象重复创建,节约空间提升效率,适用于全局只需要一个实例的场景

2. 能正确实现单例模式的实现方式总共有7种,但是只推荐使用双重检查和静态内部类两种方式进行实现。

饿汉式(静态常量)Ⅰ

  1. 构造器私有化

  2. 在类内部创建该类对象

  3. 向外提供方法获取该类对象

    public class SingleTon {
        private SingleTon(){ }
        private static final SingleTon singleTon = new SingleTon();
        public static SingleTon getInstance(){
            return singleTon;
        }
    }
    • 基于classloader机制避免了多线程同步问题

    • 没用达到懒加载的效果,如果不使用会导致内存的浪费

饿汉式(静态代码块)Ⅱ

public class SingleTon {
    private static SingleTon singleTon;
    static {
        singleTon = new SingleTon();
    }
    private SingleTon(){  
    }
    public static SingleTon getInstance(){
        return singleTon;
    }
}

懒汉式(线程不安全)Ⅲ

  1. 构造器私有化

  2. 定义该对象属性但是不创建实例

  3. 向外部提供一个公有静态方法,在调用时再创建,且之创建一次返回该对象的实例,以后再获取直接返回第一次创建的实例

    public class SingleTon {
        private static SingleTon singleTon;
        private SingleTon(){
        }
        public static SingleTon getInstance(){
            if(singleTon == null){
                return new SingleTon();
            }
            return singleTon;
        }
    }
    • 实现了懒加载的效果,但是只限于单线程,有潜在风险

懒汉式(线程安全)Ⅳ

public class SingleTon {
​
    private static SingleTon singleTon;
​
    private SingleTon(){
    }
    //加入同步处理的代码
    public static synchronized SingleTon getInstance(){
        if(singleTon == null){
            return new SingleTon();
        }
        return singleTon;
    }
}
  • 解决了线程不安全的问题

  • 效率太低

双重检查(推荐)🔴🟠🟡🟢🔵🟣🟤⚫⚪Ⅴ

  1. 在属性上必须加入volatile修饰,这样属性值一旦发生变化会立即同步,即使多线程都进入了该方法内部,也会因为双重检查不会创建多个实例。

  2. 在返回实例的方法中进行双重检查

    public class SingleTon {
        //加入volatile修饰,一但该值修改会立刻同步,可以理解为一种轻量级的同步
        private static volatile SingleTon singleTon;
    ​
        private SingleTon(){
        }
        
        public static SingleTon getInstance(){
            if(singleTon == null){
                synchronized (SingleTon.class){
                    if(singleTon == null){
                        singleTon = new SingleTon();
                    }
                }
            }
            return singleTon;
        }
    }
    • 效率高,线程安全,懒加载

    • 推荐使用

静态内部类(推荐)🔴🟠🟡🟢🔵🟣🟤⚫⚪Ⅵ

public class SingleTon {
​
    private SingleTon(){ }
​
    //在类加载时该静态内部类不会被加载,在使用返回实例方法调用其属性时才会进行装载,jvm虚拟机底层保证了其线程安全
    private static class SingleTonInstance{
        private static final SingleTon singleTon = new SingleTon();
    }
    
    public static SingleTon getInstance(){
        return SingleTonInstance.singleTon;
    }
}
  • 采用类装载机制保证了初始化实例只有一个线程

  • 调用方法才会装载静态内部类,从而完成实例化,类的静态属性只会在第一次加载时初始化,JVM保证了线程的安全(类初始化时别的线程无法进入)

  • 线程安全,实现了延迟加载(懒加载),效率高

枚举Ⅶ

  • 线程同步

  • 能防止反射和反序列化创建新对象

    public enum SingleTonEm {
        INSTANCE; //定义一个属性
    }

建造者模式

1. 创建过程稳定,配置多变的对象。类似一种自选组件来构成一个实体的过程,这些组件只需选择而无需知道如何去创建。

2. 明确该模式的四个角色

  • Product 产品角色

  • Builder 抽象建造者

  • ConcreteBuilder 具体建造者

  • Director 指挥者

代码模拟

  1. 指定产品角色,也就是最终需要得到的实体

    //probvider 产品
    public class House {
        private String baise;
        private String wall;
        private String roofed;
    ​
        public String getBaise() {
            return baise;
        }
        public void setBaise(String baise) {
            this.baise = baise;
        }
        public String getWall() {
            return wall;
        }
        public void setWall(String wall) {
            this.wall = wall;
        }
        public String getRoofed() {
            return roofed;
        }
        public void setRoofed(String roofed) {
            this.roofed = roofed;
        }
    }
  2. 指定抽象建造者,指定基本流程,这个类中的方法由具体建造者进行实现,这个过程体现该模式的   创建过程稳定    的条件。

    //Builder 抽象建造者
    public abstract class HouseBuilder {
    ​
        protected House house = new House();
    ​
        public abstract void buildBasic();
    ​
        public abstract void buildWalls();
    ​
        public abstract void rooted();
    ​
        public House buildHouse(){
            return house;
        }
    }
  3. 指定具体建造者,实现基本流程,体现   配置多变   的需求

    //ConcreteBuilder 具体建造者
    public class CommonHouse extends HouseBuilder {
        @Override
        public void buildBasic() {
            System.out.println("普通地基");
        }
    ​
        @Override
        public void buildWalls() {
            System.out.println("普通墙");
        }
    ​
        @Override
        public void rooted() {
            System.out.println("普通顶");
        }
    }
    //ConcreteBuilder 具体建造者
    public class HighHouse extends HouseBuilder {
        @Override
        public void buildBasic() {
            System.out.println("高楼地基");
        }
    ​
        @Override
        public void buildWalls() {
            System.out.println("高楼墙");
        }
    ​
        @Override
        public void rooted() {
            System.out.println("高楼顶");
        }
    }
  4. 编写指挥者,指挥者将指定生产产品的流程,当然需要通过接口聚合具体建造者类,可以通过构造器或者set方法

    //Director 指挥者
    public class HouseDirector {
    ​
        HouseBuilder houseBuilder = null;
    ​
        //两种方式传入houseBuilder
        public HouseDirector(HouseBuilder houseBuilder) {
            this.houseBuilder = houseBuilder;
        }
        public void setHouseBuilder(HouseBuilder houseBuilder) {
            this.houseBuilder = houseBuilder;
        }
    ​
        //如何建造房子流程,交给指挥者
        public House constructHouse(){
            houseBuilder.buildBasic();
            houseBuilder.buildWalls();
            houseBuilder.rooted();
            return houseBuilder.buildHouse();
        }
    }
  5. 实际调用

    public class Test {
    
        public static void main(String[] args) {
    //        在指挥者传入具体建造者指定建造的具体产品
            HouseDirector houseDirector = new HouseDirector(new HighHouse());
    //        调用方法返回对象
            House house = houseDirector.constructHouse();
        }
    }

原型模式(prototype)

1. 用原型实例指定创建对象的种类,并通过拷贝这些实例创建新的对象,原型模式默认进行的是浅拷贝,也就是通过实现cloneable接口进行clone,但是这样如果克隆对象中存在其他对象的话,对该对象属性进行修改会导致所有克隆类中该属性随之变化,也就是说,复制的知识指向该对象的指针。

浅拷贝

  1. 实现 Cloneable接口,重写clone()方法,通过super调用父类方法实现克隆

  2. 如果成员变量有引用类型,那么拷贝是是引用传递,若对该引用进行修改,则也会对拷贝的对象同时进行联动修改

public class Sheep implements Cloneable{
​
    private Integer id;
    private String name;
​
    public Sheep(Integer id, String name) {
        this.id = id;
        this.name = name;
    }
    
    @Override
    public String toString() {
        return "Sheep{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
​
    @Override
    protected Sheep clone() throws CloneNotSupportedException {
        Sheep sheep = null;
        try {
            sheep = (Sheep) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return sheep;
    }
}

深拷贝(推荐使用序列化的方式)

  1. 如果没用对象作为成员变量的话则没用必要使用深拷贝

  2. 方式一,进行深拷贝时要单独对对象进行处理,而这个对象相应也要进行拷贝处理,若其中还有对象,则需要一直处理,不推荐

  3. 方式二,通过序列化的方式进行拷贝,这个方法存在于需要拷贝的内部,通过this来将对象以对象流的方式输出。

      //深拷贝,序列化方式,推荐
        public Object deepClone(){
            ByteArrayInputStream bis = null;
            ByteArrayOutputStream bos = null;
            ObjectInputStream ois = null;
            ObjectOutputStream oos = null;
    ​
            try {
                //序列化
                bos  = new ByteArrayOutputStream();
                oos = new ObjectOutputStream(bos);
                oos.writeObject(this);//将当前对象以对象流的方式输出
    ​
                //反序列化
                bis = new ByteArrayInputStream(bos.toByteArray());
                ois = new ObjectInputStream(bis);
                Sheep copysheep = (Sheep) ois.readObject();
    ​
                return  copysheep;
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                try {
                    bis.close();
                    bos.close();
                    oos.close();
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }

    该模式意义

  • 创建新的对象较为复杂时,使用该模式可以简化对象的创建过程,能提高效率

  • 原型模式默认进行的是浅拷贝哦💙💚💛(默认使用的是clone方法,类需要实现Cloneable接口)

  • 没有重新初始化对象,而是动态获取对象的状态,即克隆的就是其当前的状态

  • 原生对象变化,此时进行克隆的对象也会相应发生变化

  • 深克隆可能需要复杂代码,推荐使用序列化方式

  • 该模式需要为每一个类配备一个克隆方法,对于新类简单,但是对于创建好的需要改造则违反了ocp原则

  • 深克隆:深复制把要复制的对象所引用的对象都复制了一遍。

    拷贝需要实现Cloneable, Serializable两个接口,重写clone方法⚠⚠⚠


网站公告

今日签到

点亮在社区的每一天
去签到