设计模式——Day04

设计模式 Day04

文章总结自B站尚硅谷

1. 原型模式

指用原型实例指定创建对象的种类,并且通过拷贝这些原型,创建新的对象。允许一个对象再创建另外一个对象可定制的对象,无需知道如何创建的细节;要发动创建的对象通过请求原型对象拷贝它们来实施创建。
缺点:每一个类都需要配备克隆方法。
案例:创建10个一模一样的🐏。

1.1 传统模式

/**
 * @date 2020/8/21 9:11
 * 传统模式下的 🐏 的属性类
 */
public class Sheep {
    private int age;
    private String name;
    private String color;

    public Sheep(int age, String name, String color) {
        this.age = age;
        this.name = name;
        this.color = color;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", color='" + color + '\'' +
                '}';
    }
}
/**
 * @date 2020/8/21 9:12
 * 普通类型下 创建多个一样的对象
 */
public class Test {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println(new Sheep(1, "tom", "white").toString());
        }
    }
}

传统模式就是直接使用new实例化;
优势:好理解,易操作。
缺点:在创建对象时,总是需要获取原始对象的属性,消耗资源;总是需要初始化对象,不能直接获取对象运行的状态,不够灵活。

1.2 原型模式

/**
 * @date 2020/8/21 9:11
 * 原型模式下的 🐏 的属性类
 */
public class Sheep implements Cloneable {
    private int age;
    private String name;
    private String color;

    public Sheep(int age, String name, String color) {
        this.age = age;
        this.name = name;
        this.color = color;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", color='" + color + '\'' +
                '}';
    }

//    重写Cloneable接口的方法,用来克隆该实例
    @Override
    protected Object clone(){
        Sheep sheep = null;
        try {
            sheep = (Sheep) super.clone();
        }catch (Exception e){
            e.printStackTrace();
        }
        return sheep;
    }
}
/**
 * @date 2020/8/21 9:12
 * 原型模式下 创建多个一样的对象
 */
public class Test {
    public static void main(String[] args) {
        Sheep sheep = new Sheep(1,"tom","white");
        System.out.println(sheep);
        for (int i = 0; i < 10; i++) {
            System.out.println(sheep.clone());
        }
    }
}

相较于普通的模式而言,原型模式使得创建新的对象更加灵活,如果对象中新增了属性,那么只需要再原对象上进行添加,其它的对象依然直接进行克隆就行。

1.3 在Spring中的使用

在Spirng中创建Bean时使用到了原型模式。

1.4 深拷贝

复制所有的基本数据类型,为引用数据类型申请存储空间,并复制每个引用数据类型成员变量所引用的对象;也就是说在进行深拷贝时要对整个对象进行拷贝。
重写clone方法或者是通过对象序列化来实现。
重写clone

//    深拷贝--重写clone
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object deep = null;
//        对基本数据类型克隆
        deep = super.clone();
//        对引用数据类型进行单独处理
        DeepProtoType deepProtoType = (DeepProtoType) deep;
//        调用引用数据类型的clone方法
        deepProtoType.setDeepCloneTarget((DeepCloneTarget) deepCloneTarget.clone());
        return deep;
    }

对象序列化:

//    深拷贝--通过对象序列化
    public Object deepClone() throws IOException {

        DeepProtoType copy = new DeepProtoType();

//        字节数组输出流
        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);
            copy = (DeepProtoType) ois.readObject();

        }catch (Exception e){
            e.printStackTrace();
        }finally {
//            关闭流
            bos.close();
            oos.close();
            bis.close();
            ois.close();
        }
        return copy;
    }

方式2相较于方式1而言更好使用,如果添加新的引用对象类型,也不需要更改方法中的代码,一劳永逸!

1.5 浅拷贝

对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行传值,也就是将该属性复制一份给新的对象;
对于数据类型是引用数据类型的成员变量,浅拷贝会进行引用传递,将该成员变量的内存地址复制给新的对象。
通过clone方法实现。

2. 构建者模式

又称生成器模式,可以将复杂对象的建造过程抽象出来,使这个抽象过程的不同实现方法可以构造出不同表现的对象。一步步的创建一个复杂对象,允许用户通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道具体构建细节。
案例:盖房子,有高低不同的房子类型,盖房子步骤分为打地基-砌墙-封顶。

2.1 传统方式

盖房子类:

/**
 * @date 2020/8/21 14:09
 *
 */
public abstract class AbstractHouse {
//    打地基
    public abstract void buildBasic();

//    砌墙
    public abstract void buildWalls();

//    封顶
    public abstract void roofed();

    public void build(){
        buildBasic();
        buildWalls();
        roofed();
    }
}

盖普通房子:

/**
 * @date 2020/8/21 14:12
 */
public class CommonHouse extends AbstractHouse {
    @Override
    public void buildBasic() {
        System.out.println("给普通房子打地基");
    }

    @Override
    public void buildWalls() {
        System.out.println("给普通房子砌墙");
    }

    @Override
    public void roofed() {
        System.out.println("给普通房子封顶");
    }
}

盖高房子:

/**
 * @date 2020/8/21 14:12
 */
public class HighBuilding extends AbstractHouse {
    @Override
    public void buildBasic() {
        System.out.println("给高房子打地基");
    }

    @Override
    public void buildWalls() {
        System.out.println("给高房子砌墙");
    }

    @Override
    public void roofed() {
        System.out.println("给高房子封顶");
    }
}

客户端:

/**
 * @date 2020/8/21 14:22
 */
public class Client {
    public static void main(String[] args) {
        CommonHouse commonHouse = new CommonHouse();
        commonHouse.build();
        HighBuilding highBuilding = new HighBuilding();
        highBuilding.build();
    }
}

优点:好理解,易操作
缺点:结构过于简单,没有设计缓存层对象,程序的扩展和维护不好,耦合性太高。

2.2 建造者模式

dao:

/**
 * @date 2020/8/21 14:40
 */
public class House {
    private String basic;
    private String wall;
    private String roofed;

    public String getBasic() {
        return basic;
    }

    public void setBasic(String basic) {
        this.basic = basic;
    }

    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;
    }
}

抽象类:提供方法让下方的不同的类别的房子进行调用。

/**
 * @date 2020/8/21 14:41
 */
public abstract class HouseBuild {
    protected House house = new House();

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

    public House build(){
        buildBasic();
        buildWall();
        roofed();
        return house;
    }

}

房子类型类:继承抽象类,根据自己的情况去实现方法。

/**
 * @date 2020/8/21 14:43
 */
public class CommonHouse extends HouseBuild {
    @Override
    public void buildBasic() {
        System.out.println("给普通房子打地基");
    }

    @Override
    public void buildWall() {
        System.out.println("给普通房子砌墙");
    }

    @Override
    public void roofed() {
        System.out.println("给普通房子封顶");
    }
}

领导者类:真正指导如何去盖房子的类

public class HouseDirector {
    HouseBuild houseBuild = null;
//    方式1 构造器 传入 houseBuild
    public HouseDirector(HouseBuild houseBuild) {
        this.houseBuild = houseBuild;
    }
    public HouseDirector(){

    }
 //    方式2 setter 传入 houseBuild
    public void setHouseBuild(HouseBuild houseBuild) {
        this.houseBuild = houseBuild;
    }

    public House constructHouse(){
        return houseBuild.build();
    }

}

客户端:

/**
 * @date 2020/8/21 14:49
 */
public class Client {
    public static void main(String[] args) {
        HouseDirector houseDirector = new HouseDirector(new HighBuilding());
        houseDirector.constructHouse();
    }
}

2.3 Spring中的使用

在Spring中的SpringBuilder类使用了构建者模式。

2.4 注意事项和细节

客户端不必知道产品内部组成的细节,将产品本身与产品创建过程解耦;每一个具体构建者都独立,而与其它的具体构建者无关,用户使用不同的具体构建者即可得到不同的产品对象;可以更加精确的控制产品的创建过程;增加新的具体创建者无需修改原有的代码。

3. 适配器模式

将某类的接口转化成客户端期望的另一个接口表示,让原本不能在一起工作的两个类协同工作,主要目的是增强兼容性!
案例:手机充电,但是手机只能接收5v,通过适配器将220v转为5v

3.1 类适配器

需要被适配的类

/**
 * @date 2020/8/21 15:18
 */
public class Voltage220V {
    public int output220V(){
        return 220;
    }
}

适配(把被适配的类转为适配这个)的类

/**
 * @date 2020/8/21 15:20
 */
public interface Voltage5V {
    public int output5V();
}

适配器:

/**
 * @date 2020/8/21 15:21
 */
public class VoltageAdapter extends Voltage220V implements Voltage5V {

    @Override
    public int output5V() {
        int src = output220V();
        int dst = src / 44;
        return dst;
    }
}

手机:

/**
 * @date 2020/8/21 15:23
 */
public class Phone {
    public void charging(Voltage5V v){
        if (v.output5V() == 5){
            System.out.println("电压5v,可以充电");
        }else {
            System.out.println("电压不是5v,不可用充电");
        }
    }
}

客户端:

/**
 * @date 2020/8/21 15:25
 */
public class Client {
    public static void main(String[] args) {
        Phone phone = new Phone();
        phone.charging(new VoltageAdapter());
    }
}

缺点:Java单继承机制,必须要求被适配的类是类,而要适配为的类就必须为一个接口。被适配的类的方法在适配器中暴露出来。
优点:可以根据要求编写被适配器的方法,使得适配器的灵活性增强了。

3.2 对象适配器

与类适配器不同的是,不再继承被适配类,而是持有它的实例,用来解决兼容性问题。
在上方类适配器代码的基础上进行修改
适配器:

/**
 * @date 2020/8/21 15:21
 */
public class VoltageAdapter  implements Voltage5V {

    private Voltage220V v;
    public VoltageAdapter(Voltage220V v) {
        this.v = v;
    }


    @Override
    public int output5V() {
        int dist = 0;
        if (v != null){
            int src = v.output220V();
            dist = src/44;
            return dist;
        }
        return dist;
    }
}

客户端:

/**
 * @date 2020/8/21 15:25
 */
public class Client {
    public static void main(String[] args) {
        Phone phone = new Phone();
        phone.charging(new VoltageAdapter(new Voltage220V()));
    }
}

类与类之间的关系由泛化(继承)变为了聚合。所有就不再要求必须要继承被适配类,目标适配类也不再必须为接口。成本更低,更加灵活。

3.3 接口适配器

当不需要全部接口提供的方法时,可以设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现的空方法,该抽象的子类可以有选择的覆盖父类中某些方法来实现需求;适用于与一个接口不想使用其所有方法的情况。
接口:

/**
 * @date 2020/8/21 15:49
 */
public interface IInterface {
    public void m1();
    public void m2();
    public void m3();
    public void m4();
}

抽象类:

/**
 * @date 2020/8/21 15:50
 */
public class AbsAdapter implements IInterface {
    @Override
    public void m1() {

    }

    @Override
    public void m2() {

    }

    @Override
    public void m3() {

    }

    @Override
    public void m4() {

    }
}

客户端:
在调用时,使用匿名客户端去重写抽象类实现的接口中的方法。

/**
 * @date 2020/8/21 15:50
 */
public class Client {
    public static void main(String[] args) {
        AbsAdapter absAdapter = new AbsAdapter() {
            @Override
            public void m1() {
                System.out.println("m1 -- running");
            }
        };
        absAdapter.m1();
    }
}

3.4 SpringMVC

在SpingMVC框架的HandlerAdapter中用到了适配器模式。

4. 桥接模式

将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变;基于最喜小的设计原则,听过封装、聚合及继承为让不同的类承担不同的职责。
案例:手机有不同的品牌和不同的打开方式。
提供方法的接口:

/**
 * @date 2020/8/21 16:46
 */
public interface Brand {
    void open();
    void close();
    void call();
}

品牌:

/**
 * @date 2020/8/21 16:47
 */
public class Apple implements Brand{
    @Override
    public void open() {
        System.out.println("苹果手机开机");
    }

    @Override
    public void close() {
        System.out.println("苹果收件关机");
    }

    @Override
    public void call() {
        System.out.println("苹果手机打电话");
    }
}
/**
 * @date 2020/8/21 16:47
 */
public class HuaWei implements Brand{
    @Override
    public void open() {
        System.out.println("华为手机开机");
    }

    @Override
    public void close() {
        System.out.println("华为收件关机");
    }

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

打开方式:

/**
 * @date 2020/8/21 16:54
 */
public class FoldedPhone extends Phone{
    public FoldedPhone(Brand brand) {
        super(brand);
    }

    @Override
    protected void open() {
        super.open();
        System.out.println("折叠样式的手机");
    }

    @Override
    protected void close() {
        super.close();
        System.out.println("折叠样式的手机");
    }

    @Override
    protected void call() {
        super.call();
        System.out.println("折叠样式的手机");
    }
}

抽象类:

/**
 * @date 2020/8/21 16:53
 */
public abstract class Phone {
    private Brand brand;

    public Phone(Brand brand) {
        this.brand = brand;
    }

    protected void open(){
        this.brand.open();
    }
    protected void close(){
        this.brand.close();
    }
    protected void call(){
        this.brand.call();
    }

}

客户端:

/**
 * @date 2020/8/21 16:58
 */
public class Client {
    public static void main(String[] args) {
        FoldedPhone foldedPhone = new FoldedPhone(new Apple());
        foldedPhone.open();
        foldedPhone.call();
        foldedPhone.close();
    }
}

这种方式,如果要新增一个新的手机品牌或者手机打开方式,只需要添加相应的类即可,不需要改变其它的代码。
这种方式在JDBC的Driver接口中有使用
优点:

  1. 实现了抽象和实现的分离,从而极大的提供了系统的灵活性,有助于系统的分层设计,从而产生更好的结构化体系;
  2. 代替了多层继承方案,减少了子类的个数,降低系统的管理和维护成本;
  3. 对于系统高层的部分,只需要知道抽象部分和实现部分的接口就可以,其它的部分由具体业务来完成;

缺点:增加了系统的理解和设计难度,由于聚合关联关系在抽象层,要求开发者针对抽象进行设计和编程;要求正确的识别出系统两个独立变化的维度,有一定的局限性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值