设计模式:(三)原型、建造者、适配器、桥接模式

2023-11-15

一、原型模式(Prototype)

1、概述

1、定义:用原型实例指定了要创建对象的种类,并且通过拷贝这些原型,创建新的对象。原型模式是一种创建型设计模式,允许一个对象再创建另外一个可定制的对象, 无需知道如何创建的细节。
2、工作原理:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建,即对象.clone()。形象的理解:孙大圣拔出猴毛, 变出其它孙大圣。
3、在Spring框架中创建Bean的时候使用到原型模式。
4、源对象给外部提供一个克隆对象进行使用,克隆对象有任何修改,都不会影响源对象

2、主要角色

1、抽象原型类:规定了具体原型对象必须实现的接口。
2、具体原型类:实现抽象原型类的clone()方法,它是可以被复制的对象。
3、访问类:使用具体原型类中的clone()方法来复制新的对象。

3、传统方式完成复制对象

@Data
@AllArgsConstructor
public class Sheep {
    private String name;
    private Integer age;
    private String color;
}

public class Test {
    public static void main(String[] args) {
        Sheep sheep = new Sheep("克隆羊",1,"白色");
        Sheep sheep1 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep2 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep3 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        Sheep sheep4 = new Sheep(sheep.getName(), sheep.getAge(), sheep.getColor());
        System.out.println(sheep);
        System.out.println(sheep1);
        System.out.println(sheep2);
        System.out.println(sheep3);
        System.out.println(sheep4);
    }
}

/**
 * 运行结果
 */
Sheep(name=克隆羊, age=1, color=白色)
Sheep(name=克隆羊, age=1, color=白色)
Sheep(name=克隆羊, age=1, color=白色)
Sheep(name=克隆羊, age=1, color=白色)
Sheep(name=克隆羊, age=1, color=白色)
1、优缺点说明
  • 优点是比较好理解,简单易操作。
  • 在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低。
  • 总是需要重新初始化对象,而不是动态地获得对象运行时的状态,不够灵活。
2、改进的方法:
  • Java中Object类是所有类的基类,Object类提供了一个clone()方法,该方法可以将一个Java对象复制一份,但是需要实现clone的Java类必须要实现一个接口Cloneable, 该接口表示该类能够复制且具有复制的能力。

4、原型模式实现

/**
 * 原型类
 */
@Data
@AllArgsConstructor
public class Sheep implements Cloneable {
    private String name;
    private Integer age;
    private String color;

    /**
     * 克隆该实例使用默认的clone方法来完成
     * @return
     */
    @Override
    protected Object clone() {
        Sheep sheep = null;
        try {
            sheep = (Sheep)super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println(e.getMessage());
        }
        return sheep;
    }
}
/**
 * 客户端调用类
 */
public class Test {
    public static void main(String[] args) {
        Sheep sheep = new Sheep("克隆羊",1,"白色");
        System.out.println("原型模式完成对象的创建");
        Sheep sheep1 = (Sheep) sheep.clone();
        Sheep sheep2 = (Sheep) sheep.clone();
        Sheep sheep3 = (Sheep) sheep.clone();
        Sheep sheep4 = (Sheep) sheep.clone();
        System.out.println(sheep);
        System.out.println(sheep1);
        System.out.println(sheep2);
        System.out.println(sheep3);
        System.out.println(sheep4);
    }
}
/**
 * 运行结果:
 * 原型模式完成对象的创建
 * Sheep(name=克隆羊, age=1, color=白色)
 * Sheep(name=克隆羊, age=1, color=白色)
 * Sheep(name=克隆羊, age=1, color=白色)
 * Sheep(name=克隆羊, age=1, color=白色)
 * Sheep(name=克隆羊, age=1, color=白色)
 */

5、浅拷贝

1、对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象
2、对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值
3、上述示例就是浅拷贝,浅拷贝是使用默认的 clone() 方法来实现。

6、深拷贝

1、复制对象的所有基本数据类型的成员变量值。
2、为所有引用数据类型的成员变量申请存储空间,并复制每个引用数据类型成员变量所引用的对象,直到该对象可达到的所有对象。也就是说,对象进行深拷贝要对整个对象进行拷贝。
3、深拷贝要两种实现方式:
  • 重写clone方法来实现深拷贝。
  • 通过对象序列化实现深拷贝(推荐使用)。

7、重写clone方法来实现深拷贝

public class DeepCloneableTarget implements Cloneable, Serializable {
    private static final long serialVersionUID = 1L;

    private String cloneName;

    private String cloneClass;

    public DeepCloneableTarget(String cloneName, String cloneClass) {
        this.cloneName = cloneName;
        this.cloneClass = cloneClass;
    }

    /**
     * 因为该类的属性都是string,因此使用默认的clone完成即可
     * @return
     * @throws CloneNotSupportedException
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public class DeepProtoType implements Cloneable, Serializable {
    private String name;

    /**
     * 引用类型
     */
    private DeepCloneableTarget deepCloneableTarget;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public DeepCloneableTarget getDeepCloneableTarget() {
        return deepCloneableTarget;
    }

    public void setDeepCloneableTarget(DeepCloneableTarget deepCloneableTarget) {
        this.deepCloneableTarget = deepCloneableTarget;
    }

    /**
     * 重写clone方法实现深拷贝
     * @return
     * @throws CloneNotSupportedException
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object deep = null;
        //完成对基本数据类型(属性)和String的克隆
        deep = super.clone();
        //对引用类型的属性,进行单独处理
        DeepProtoType deepProtoType = (DeepProtoType) deep;
        deepProtoType.deepCloneableTarget = (DeepCloneableTarget) deepCloneableTarget.clone();
        return deepProtoType;
    }
}

public class Test {
    public static void main(String[] args) throws Exception {
        DeepProtoType p1 = new DeepProtoType();
        p1.setName("重写clone");
        p1.setDeepCloneableTarget(new DeepCloneableTarget("大牛","小牛"));
        //调用深拷贝
        DeepProtoType p2 = (DeepProtoType) p1.clone();
        System.out.println("p1.name = " + p1.getName() + ",p1 = " + p1.hashCode() + ",p1.deepCloneableTarget = " + p1.getDeepCloneableTarget().hashCode());
        System.out.println("p2.name = " + p2.getName() + ",p2 = " + p2.hashCode() + ",p2.deepCloneableTarget = " + p2.getDeepCloneableTarget().hashCode());
    }
}

/**
 * 运行结果:
 * p1.name = 重写clone,p1 = 1956725890,p1.deepCloneableTarget = 356573597
 * p2.name = 重写clone,p2 = 1735600054,p2.deepCloneableTarget = 21685669
 */
注意:如果在类上加了@Data注解,那么此类实例化的对象,如果没有为属性设置值,则两个实例化对象的hashCode()是相等的

8、通过对象序列化实现深拷贝

public class DeepProtoType implements Cloneable, Serializable {
    private String name;

    /**
     * 引用类型
     */
    private DeepCloneableTarget deepCloneableTarget;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public DeepCloneableTarget getDeepCloneableTarget() {
        return deepCloneableTarget;
    }

    public void setDeepCloneableTarget(DeepCloneableTarget deepCloneableTarget) {
        this.deepCloneableTarget = deepCloneableTarget;
    }

    /**
     * 对象序列化实现深拷贝
     * @return
     * @throws CloneNotSupportedException
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        //创建流对象
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        try {
            //序列化
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            //将当前这个对象以对象流的方式输出
            oos.writeObject(this);
            //反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            DeepProtoType deepProtoType = (DeepProtoType) ois.readObject();
            return deepProtoType;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

9、优缺点说明

1、优点:
  • 创建新的对象比较复杂时,可以利用原型模式简化对象的创建过程,同时也能够提高效率。
  • 不用重新初始化对象,而是动态地获得对象运行时的状态。
  • 如果原始对象发生变化(增加或者减少属性),其它克隆对象的也会发生相应的变化, 无需修改代码。
2、缺点:
  • 需要为每一个类配备一个克隆方法,这对全新的类来说不是很难,但对已有 的类进行改造时,需要修改其源代码,违背了ocp(开闭)原则。
  • 当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。

二、建造者模式

1、概述

1、定义:建造者模式(Builder Pattern) 又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。
2、建造者(Builder)模式和工厂模式的关注点不同:建造者模式注重零部件的组装过程,而工厂方法模式更注重零部件的创建过程,但两者可以结合使用。(比如建房:需要打桩、砌墙、封顶)。

2、主要角色

1、产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个零部件。
2、抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口或抽象类,通常还包含一个返回复杂产品的方法。
3、具体建造者(Concrete Builder):实现抽象建造者的接口,完成复杂产品的各个部件的具体创建方法。
4、指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。

3、建造者模式实现

/**
 * 产品类
 */
@Data
public class House {
    private String baise;

    private String wall;

    private String roofed;
}
/**
 * 抽象建造者类
 */
public abstract class HouseBuilder {
    protected House house = new House();

    public abstract void buildBasic();
    public abstract void buildWalls();
    public abstract void roofed();

    /**
     * 返回产品对象
     * @return
     */
    public House getResult() {
        return house;
    }
}
/**
 * 具体建造者类
 */
public class CommonHouse extends HouseBuilder {
    @Override
    public void buildBasic() {
        house.setBaise("打地基");
    }

    @Override
    public void buildWalls() {
        house.setWall("砌墙");
    }

    @Override
    public void roofed() {
        house.setRoofed("封顶");
    }
}
/**
 * 指挥者,指定创建流程,返回产品
 */
public class HouseDirector {
    private HouseBuilder houseBuilder;

    /**
     * 构造器传入houseBuilder
     * @param houseBuilder
     */
    public HouseDirector(HouseBuilder houseBuilder) {
        this.houseBuilder = houseBuilder;
    }

    /**
     * 产品构建与组装方法
     * @return
     */
    public House constructHouse() {
        houseBuilder.buildBasic();
        houseBuilder.buildWalls();
        houseBuilder.roofed();
        return houseBuilder.getResult();
    }
}
/**
 * 客户端调用类
 */
public class Test {
    public static void main(String[] args) {
        // 创建具体建造者
        CommonHouse commonHouse = new CommonHouse();
        // 创建指挥
        HouseDirector houseDirector = new HouseDirector(commonHouse);
        // 调用构建方法
        House house = houseDirector.constructHouse();
        System.out.println(house);
    }
}
/**
 * 运行结果:
 * House(baise=打地基, wall=砌墙, roofed=封顶)
 */

4、建造者模式在JDK的应用

1、java.lang.StringBuilder中的建造者模式
  • Appendable接口定义了多个append方法(抽象方法),即Appendable为抽象建造者, 定义了抽象方法。
  • AbstractStringBuilder实现了Appendable接口方法,这里的AbstractStringBuilder已经是建造者,只是不能实例化。
  • StringBuilder即充当了指挥者角色,同时充当了具体的建造者,建造方法的实现是由AbstractStringBuilder完成,而StringBuilder继承了AbstractStringBuilder 。

5、优缺点说明

1、优点:
  • 封装性好,构建和表示分离。
  • 扩展性好,各个具体的建造者相互独立,有利于系统的解耦。
  • 客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险。
2、缺点:
  • 建造者模式所创建的产品一般具有较多的共同点,产品的组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,这限制了其使用范围。
  • 如果产品的内部变化复杂,如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大。

三、适配器模式

1、概述

1、定义:适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)。
2、分类:
  • 类适配器模式。
  • 对象适配器模式。
  • 接口适配器模式。
  • 注意:类适配器模式的耦合度比对象的高
3、工作原理:将一个类的接口转换为另一种接口,让原本接口不兼容的类可以兼容。从用户角度看不到被适配者,是解耦的,用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法,用户收到反馈结果,感觉只是和目标接口交互。

2、主要角色

1、目标接口(Target):可以是抽象类或接口,当前系统业务期望直接用的接口。
2、被适配者(Adaptee):它是被访问和适配的现存组件库中的组件接口。
3、适配器(Adapter):它是一个转换器,通过继承或引用被适配者的对象,把适配者接口转换成目标接口,让系统按目标接口的格式访问被适配者。

3、类适配器模式实现

Adapter类通过继承Adaptee类,实现Target类接口完成适配。
/**
 * 被适配的类
 */
public class Adaptee {
    public int outPut220v() {
        int num = 220;
        System.out.println("原始电压为" + num + "伏特");
        return num;
    }
}

/**
 * 目标接口
 */
public interface Target {
    public int outPut5v();
}

/**
 * 类适配器
 */
public class ClassAdapter extends Adaptee implements Target {
    @Override
    public int outPut5v() {
        //获取220V电压
        int outPut220v = outPut220v();
        //转换成目标接口所需要的
        return outPut220v / 44;
    }
}

/**
 * 客户端调用类
 */
public class Test {
    public static void main(String[] args) {
        System.out.println("类适配器模式");
        Target target = new ClassAdapter();
        System.out.println("手机能接受电压为" + target.outPut5v() + "伏特");
    }
}

/**
 * 运行结果:
 * 类适配器模式
 * 原始电压为220伏特
 * 手机能接受电压为5伏特
 */
1、优缺点说明:
  • Java是单继承机制,所以类适配器需要继承Adaptee类这一点算是一个缺点,因为这要求Target必须是接口,有一定局限性。
  • Adaptee类的方法在Adapter中都会暴露出来,也增加了使用的成本。
  • 由于其继承了Adaptee类,所以它可以根据需求重写Adaptee类的方法,使得Adapter的灵活性增强了。

4、对象适配器模式实现

1、基本思路和类的适配器模式相同,只不过实现方式不同,只是将Adapter类作修改,不是继承Adaptee类,而是持有Adaptee类的实例,以解决兼容性的问题。 即:持有Adaptee类,实现Target类接口,完成Adaptee -> Target的适配。
2、根据“合成复用原则”,在系统中尽量使用组合关系来替代继承关系。
3、对象适配器模式是适配器模式常用的一种。
/**
 * 被适配的类
 */
public class Adaptee {
    public int outPut220v() {
        int num = 220;
        System.out.println("原始电压为" + num + "伏特");
        return num;
    }
}

/**
 * 目标接口
 */
public interface Target {
    public int outPut5v();
}

/**
 * 类适配器
 */
public class ClassAdapter implements Target {
    /**
     * 将Adaptee聚合到当前类中,而不再是通过继承方式
     */
    private Adaptee adaptee;

    public ClassAdapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public int outPut5v() {
        //获取220V电压
        int outPut220v = adaptee.outPut220v();
        //转换成目标接口所需要的
        return outPut220v / 44;
    }
}

/**
 * 客户端调用类
 */
public class Test {
    public static void main(String[] args) {
        System.out.println("对象适配器模式");
        //被适配类的对象
        Adaptee adaptee = new Adaptee();
        Target target = new ClassAdapter(adaptee);
        System.out.println("手机能接受电压为" + target.outPut5v() + "伏特");
    }
}
1、优缺点说明:
  • 把多个不同的被适配者适配到同一个目标,也就是说,同一个适配器可以把被适配者类和他的子类都适配到目标接口。
  • 与类适配器模式相比,要想重写被适配者类的方法就不容易。

5、接口适配器模式实现

1、接口适配器模式称为适配器模式(Default Adapter Pattern)或缺省适配器模式。
2、当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求。
/**
 * 被适配的类
 */
public class Adaptee {
    public int outPut220v() {
        int num = 220;
        System.out.println("原始电压为" + num + "伏特");
        return num;
    }
}

/**
 * 接口可有更多的抽象方法,而不仅仅局限于一种,比作万能的适配类
 * 输出220v、110v、20v、5v电压
 */
public interface OutputAdapter {
    public int outPut220V();
    public int outPut110V();
    public int outPut20V();
    public int outPut5V();
}

/**
 * 中间适配器,用于实现默认的接口方法,等着子类去实现需要的方法。
 */
public abstract class AbstractAdapter implements OutputAdapter {
    protected Adaptee adaptee;

    public AbstractAdapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public int outPut220V() {
        return 0;
    }

    @Override
    public int outPut110V() {
        return 0;
    }

    @Override
    public int outPut20V() {
        return 0;
    }

    @Override
    public int outPut5V() {
        return 0;
    }
}

/**
 * 5V的具体实现
 */
public class Adapter5V extends AbstractAdapter {

    public Adapter5V(Adaptee adaptee) {
        super(adaptee);
    }

    @Override
    public int outPut5V() {
        int outPut = 0;
        if (adaptee != null) {
            outPut = adaptee.outPut220v() / 44;
        }
        return outPut;
    }
}

/**
 * 客户端调用类
 */
public class Test {
    public static void main(String[] args) {
        //已经有具体实现类的调用方式
        Adapter5V adapter5V = new Adapter5V(new Adaptee());
        System.out.println("已经有具体实现类的调用方式,手机能接受电压为" + adapter5V.outPut5V() + "伏特");
        //没有具体的实现类的调用方式
        AbstractAdapter abstractAdapter = new AbstractAdapter(new Adaptee()) {
            @Override
            public int outPut110V() {
                int outPut = 0;
                if (adaptee != null) {
                    outPut = adaptee.outPut220v() / 2;
                }
                return outPut;
            }
        };
        System.out.println("没有具体的实现类的调用方式,笔记本能接受电压为" + abstractAdapter.outPut110V() + "伏特");
    }
}

/**
 * 运行结果:
 * 原始电压为220伏特
 * 已经有具体实现类的调用方式,手机能接受电压为5伏特
 * 原始电压为220伏特
 * 没有具体的实现类的调用方式,笔记本能接受电压为110伏特
 */

6、优缺点说明

1、优点:
  • 将目标类和被适配者类解耦
  • 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性
  • 灵活性和扩展性都非常好,符合开闭原则。
2、缺点:
  • 适配器编写过程需要结合业务场景全面考虑,可能会增加系统的复杂性。
  • 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。

四、桥接模式

1、概述

1、定义:桥接(Bridge)模式是将抽象与实现分离,使它们可以独立变化,它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
2、它是一种结构型设计模式,Bridge模式基于类的最小设计原则,通过使用封装、聚合及继承等行为让不同的类承担不同的职责。
3、它的主要特点是把抽象(Abstraction)与行为实现 (Implementation)分离开来,从而可以保持各部分的独立性以及应对他们的功能扩展。

2、主要角色

1、抽象类(Abstraction):定义抽象类,并包含一个实现类接口类型的对象引用。
2、扩充抽象类(Refined Abstraction):在系统中可能不止一个,实现抽象类定义的业务方法,是抽象类的子类,通过组合关系调用实现类接口中的业务方法。
3、实现类接口(Implementor):定义实现类接口,供扩充抽象类调用。
4、具体实现类(ConcreteImplementor):在系统中可能不止一个,给出实现类接口的具体实现。

3、桥接模式实现

/**
 * 实现类接口(手机品牌类)
 */
public interface BrandImplementor {
    public void open();

    public void close();

    public void call();
}
/**
 * 具体实现类(小米手机)
 */
public class Xiaomi implements BrandImplementor {
    @Override
    public void open() {
        System.out.println("小米手机开机~");
    }

    @Override
    public void close() {
        System.out.println("小米手机关机~");
    }

    @Override
    public void call() {
        System.out.println("小米手机打电话~");
    }
}

/**
 * 具体实现类(Vivo手机)
 */
public class Vivo implements BrandImplementor {
    @Override
    public void open() {
        System.out.println("Vivo手机开机~");
    }

    @Override
    public void close() {
        System.out.println("Vivo手机关机~");
    }

    @Override
    public void call() {
        System.out.println("Vivo手机打电话~");
    }
}
/**
 * 抽象类:手机抽象类
 */
public abstract class PhoneAbstraction {
    //组合品牌
    private BrandImplementor brandImplementor;

    /**
     * 构造方法
     * @param brandImplementor
     */
    public PhoneAbstraction(BrandImplementor brandImplementor) {
        this.brandImplementor = brandImplementor;
    }

    public void open() {
        brandImplementor.open();
    }

    public void close() {
        brandImplementor.close();
    }

    public void call() {
        brandImplementor.call();
    }
}
/**
 * 扩充抽象类A,实现PhoneAbstraction中的方法
 */
public class RefinedAbstractionA extends PhoneAbstraction {

    /**
     * 构造方法
     * @param brandImplementor
     */
    public RefinedAbstractionA(BrandImplementor brandImplementor) {
        super(brandImplementor);
    }

    @Override
    public void open() {
        System.out.println("6 + 128G内存");
        super.open();
    }

    @Override
    public void close() {
        System.out.println("6 + 128G内存");
        super.close();
    }

    @Override
    public void call() {
        System.out.println("6 + 128G内存");
        super.call();
    }
}

/**
 * 扩充抽象类B,实现PhoneAbstraction中的方法
 */
public class RefinedAbstractionB extends PhoneAbstraction {

    /**
     * 构造方法
     * @param brandImplementor
     */
    public RefinedAbstractionB(BrandImplementor brandImplementor) {
        super(brandImplementor);
    }

    @Override
    public void open() {
        System.out.println("8 + 256G内存");
        super.open();
    }

    @Override
    public void close() {
        System.out.println("8 + 256G内存");
        super.close();
    }

    @Override
    public void call() {
        System.out.println("8 + 256G内存");
        super.call();
    }
}
/**
 * 客户端调用类
 */
public class Test {
    public static void main(String[] args) {
        RefinedAbstractionA a = new RefinedAbstractionA(new Xiaomi());
        a.open();
        a.call();
        a.close();
        System.out.println("=========================================");
        RefinedAbstractionB b = new RefinedAbstractionB(new Vivo());
        b.open();
        b.call();
        b.close();
    }
}
/**
 * 运行结果:
 * 6 + 128G内存
 * 小米手机开机~
 * 6 + 128G内存
 * 小米手机打电话~
 * 6 + 128G内存
 * 小米手机关机~
 * =========================================
 * 8 + 256G内存
 * Vivo手机开机~
 * 8 + 256G内存
 * Vivo手机打电话~
 * 8 + 256G内存
 * Vivo手机关机~
 */

4、优缺点说明

1、优点:
  • 抽象与实现分离,扩展能力强,符合开闭原则,符合合成复用原则。
  • 对于系统的高层部分,只需要知道抽象部分和实现部分的接口就可以了,其它的部分由具体业务来完成。
  • 桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本。
2、缺点:
  • 由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,能正确地识别出系统中两个独立变化的维度,这增加了系统的理解与设计难度。

5、适用场景

1、需要在抽象类和具体类之间增加更多灵活性。
2、类中存在多个独立变化的维度,且这些维度都需要独立进行扩展。
3、不希望使用多层继承导致系统类的个数急剧增加。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

设计模式:(三)原型、建造者、适配器、桥接模式 的相关文章

  • 一个在PDO数据绑定上踩的坑

    不经常用PDO 一直不知道数据绑定的时候的一个细节 不可使用同一个变量名为不同的符号进行数据绑定 因为这样的话会导致在执行execute 时 不同符号都绑定了同一个值 最后一个赋予这个变量名的值 比如 letters array a b c

随机推荐

  • mysql商品实现多退少补_(转)mysql处理高并发,防止库存超卖

    原文链接 http blog csdn net caomiao2006 article details 38568825 今天王总又给我们上了一课 其实mysql处理高并发 防止库存超卖的问题 在去年的时候 王总已经提过 但是很可惜 即使当
  • JS逆向教程第一发

    JS逆向教程第一发 项目简介 本文适合爬虫新手初步接触js逆向时进行阅读 初步接触JS逆向 并不是很熟悉逆向操作的 可以以本文为参考 初步入门 如果有大佬 也欢迎批评指正 本文以企名科技这个网站为例 初步介绍JS逆向的基本操作 环境准备 此
  • Ubuntu20.04.4LTS安装CUDA11.6,cuDNN,TensorRT,PyCUDA,PyTorch

    从0到1的cuda安装 前言 这个CUDA是否选择最新版本和你是否用pytorch有关 你先去Pytorch查一下它最高支持的cuda版本是啥 不是一般都不是最新版 0 cuda卸载 1 cuda最新版本安装 1 检查所需版本 2 具体操作
  • JS-cookie和正则表达式

    一 cookie 1 什么是cookie 会话跟踪技术 2 作用 验证身份 存储信息 3 特点 大小限制 最多存4k 每个域下只能存50个cookie 有时间限制 只能存放字符串 只能访问同一个域内的cookie 4 读取和设置cookie
  • 【网络爬虫&自动化测试】Playwriht系列文章一

    Playwriht系列文章 一 Playwright Playwriht系列文章 一 一 Playwright是什么 二 使用步骤 1 基本使用 2 Browser BrowserType BrowserType launch kwargs
  • list容器模拟实现

    个人简介 作者简介 大家好 我是菀枯 支持我 点赞 收藏 留言 格言 不要在低谷沉沦自己 不要在高峰上放弃努力 前言 好久不见啊 今天我们的任务是自己从头到尾实现一个STL中的list容器 list的实现 list的介绍 之前我们实现了ST
  • JS Object类型变量赋值失败(普通模式静默失败/严格模式报错/触发setter)

    JS Object类型变量赋值 静默失败 严格模式报错 在回顾 你不知道的JavaScript 上卷的 第五章 5 1 2 属性设置和屏蔽 时候看到一个知识点 开发者一般情况都认为如果对 Prototype 上已经存在的属性进行 Put 赋
  • java sum_java math.sum

    Math random 100 num2 int Math random 100 sum 0 temp int Math random 10 2 if temp 0 sum sum m 除法运算 if preOperater equals
  • mysql 字段类型 负数_MySQL字段类型详解

    MySQL支持大量的列类型 它可以被分为3类 数字类型 日期和时间类型以及字符串 字符 类型 本节首先给出可用类型的一个概述 并且总结每个列类型的存储需求 然后提供每个类中的类型性质的更详细的描述 概述有意简化 更详细的说明应该考虑到有关特
  • UNIX环境高级编程 学习笔记 第四章 文件和目录

    4个stat函数 stat函数返回名为参数pathname的文件有关的信息结构 fstat函数获得已经在描述符fd上打开的文件的信息 lstat函数的参数pathname如果是一个符号链接文件 它返回符号链接的有关信息 而非该符号链接引用的
  • 解除BitLocker加密

    问题描述 硬盘设置BitLocker加密后 系统异常时必须输入BitLocker恢复密钥 才能使用windows高级功能进行修复 启用BitLocker硬盘加密后一定要保存好BitLocker密钥 加密状态下windows密码无法强制破解
  • Xilinx ISE 14.7 设置编辑器字体大小

    左上角 edit preference
  • 2013年5月23日(7-9ddraw裁剪器)

    进行裁剪步骤有4个 1 创建ddraw裁减器对象 CreateClipper 0 LPDIRECTDRAWCLIPPER NULL 2 创建裁剪队列 即RECT结构的矩形列表 blitter硬件只绘制裁剪区内的内容 其余的都剪掉 裁剪队列需
  • MATLAB对原始光斑进行运算

    clc clear all close all I imread ban jpg figure 1 subplot 1 3 1 imshow I 显示原始光斑 title 原始光斑 I1 imadjust I 0 2 0 6 subplot
  • liunx,centos7 下安装部署 Jenkins 持续集成工具,war包方式安装Jenkins

    Jenkins是一个开源软件项目 是基于Java开发的一种持续集成工具 用于监控持续重复的工作 旨在提供一个开放易用的软件平台 使软件的持续集成变成可能 下载地址 https bitnami com stack jenkins instal
  • QT:通过按钮调用qpaintEvent事件实现自定义绘图

    摘要 最近碰到了一个需要在qt中完成二维图像变换的作业 想到刚接触qt时学过qpainter来进行绘画操作 立马就着手码起来 作为一个小白 马上就发现了相应的问题 由于qt内部机制 绘画事件 paintEvent 仅允许在绘画事件下进行 比
  • 数据挖掘实战(以kaggle为例)

    第一课 第1课 机器学习算法 工具与流程概述 主要分为以下内容进行讲述 机器学习工业应用领域 机器学习常用算法 机器学习常用工具 解决问题流程 数据的处理比模型更为重要 老师的博客 内容很详细 数据预处理 有时候可以一个feature一个f
  • PCL中点云分割算法简析

    文章目录 前言 一 点云分割算法简介 1 1 基于RANSAC的点云分割 1 2 基于聚类的点云分割 1 2 1 欧式聚类分割 1 3 基于深度学习的点云分割 二 算法示例 2 1 基于RANSAC的平面分割 2 2 欧式聚类 2 3 基于
  • Linux 开源的高性能稳定服务器操作系统入门

    我们研发项目 写代码时 使用的一般都是windows或者是MAC的操作系统 作为本地的用户使用系统 而Linux一般都是放在服务器上运行的一种高效稳定的服务器系统 我们还是无法完全避免对于Linux的接触和使用 尤其是在一些中小型企业 一个
  • 设计模式:(三)原型、建造者、适配器、桥接模式

    一 原型模式 Prototype 1 概述 1 定义 用原型实例指定了要创建对象的种类 并且通过拷贝这些原型 创建新的对象 原型模式是一种创建型设计模式 允许一个对象再创建另外一个可定制的对象 无需知道如何创建的细节 2 工作原理 通过将一