设计模式之工厂模式、适配器模式、桥接模式

目录

工厂模式为什么要替代new的方式初始化对象呢?

Spring中的javabean是什么时候初始化的?


工厂模式(Factory)

百度搜索一下可以看到是:

简单来讲就是将原本的直接new变成了由一个专门的工厂类来new。

工厂模式为什么要替代new的方式初始化对象呢?

案例给你答案:

需求:一个披萨制作的项目,要求该项目易于扩展维护;

1、能够生产出美式披萨、中式披萨...

2、披萨制作过程包含原材料准备、烘培、切割、打包

3、可生成披萨订单

 使用工厂模式前:

AmericaPizza(美式披萨)

package com.penghua.factory;

/**
 * 美式披萨
 */
class AmericaPizza implements Pizza {
    public AmericaPizza() {
        this.pre();
        this.bake();
        this.cut();
        this.box();
    }

    @Override
    public void pre() {
        System.out.println("美式 披萨材料准备...");
    }

    @Override
    public void bake() {
        System.out.println("美式 披萨烘培...");
    }

    @Override
    public void cut() {
        System.out.println("美式 披萨的切片...");
    }

    @Override
    public void box() {
        System.out.println("美式 披萨包装盒包装");
    }
}
ChinesePizza(中式披萨)
package com.penghua.factory;

/**
 * 中式披萨
 */
class ChinesePizza implements Pizza {

    public ChinesePizza() {
        this.pre();
        this.bake();
        this.cut();
        this.box();
    }

    @Override
    public void pre() {
        System.out.println("中式披萨材料准备...");
    }

    @Override
    public void bake() {
        System.out.println("中式披萨烘培...");
    }

    @Override
    public void cut() {
        System.out.println("中式披萨的切片...");
    }

    @Override
    public void box() {
        System.out.println("中式披萨包装盒包装");
    }
}
Client(事件)
package com.penghua.factory;

public class Client {
    public static void main(String[] args) {
        Pizza chinaPizza = PizzaFactory.createPizza(1);

        System.out.println("=============================");
        Pizza americaPizza = PizzaFactory.createPizza(2);

        System.out.println("=============================");
        Pizza japanPizza = PizzaFactory.createPizza(3);
    }
}
JapanPizza(日式披萨)
package com.penghua.factory;

/**
 * 日式披萨
 */
class JapanPizza implements Pizza {
    public JapanPizza() {
        this.pre();
        this.bake();
        this.cut();
        this.box();
    }

    @Override
    public void pre() {
        System.out.println("日式 披萨材料准备...");
    }

    @Override
    public void bake() {
        System.out.println("日式 披萨烘培...");
    }

    @Override
    public void cut() {
        System.out.println("日式 披萨的切片...");
    }

    @Override
    public void box() {
        System.out.println("日式 披萨包装盒包装");
    }
}

Pizza(父类:披萨)

package com.penghua.factory;

/**
 * 披萨(父类)
 */
public interface Pizza {
    void pre();

    void bake();

    void cut();

    void box();
}

PizzaFactory(披萨工厂类)

package com.penghua.factory;

/**
 * 披萨工厂类
 */
public class PizzaFactory {
    public static Pizza createPizza(int no){
        switch (no){
            case 1:
                return new ChinesePizza();
            case 2:
                return new AmericaPizza();
            case 3:
                return new JapanPizza();
        }
        return null;
    }
}

虽然这样也有工程类,但是实际上披萨的制作在对应的披萨的构造方法时使用,这样会导致如果有子类继承该方法时也会执行操作。通俗易懂的来说:你要吃菜,那么你要先买菜、洗菜、做菜然后才能吃。但是你去别人店里同样是吃菜就不需要那么麻烦,就不需要那么多步骤,老板直接做菜就行。但是如果你继承的话就会把不需要的步骤也执行了。

下面是工厂模式:

AmericaPizza(美式披萨)

package com.penghua.factory;

/**
 * 美式披萨
 */
class AmericaPizza implements Pizza {

    @Override
    public void pre() {
        System.out.println("美式 披萨材料准备...");
    }

    @Override
    public void bake() {
        System.out.println("美式 披萨烘培...");
    }

    @Override
    public void cut() {
        System.out.println("美式 披萨的切片...");
    }

    @Override
    public void box() {
        System.out.println("美式 披萨包装盒包装");
    }
}
ChinesePizza(中式披萨)
package com.penghua.factory;

/**
 * 中式披萨
 */
class ChinesePizza implements Pizza {

    @Override
    public void pre() {
        System.out.println("中式披萨材料准备...");
    }

    @Override
    public void bake() {
        System.out.println("中式披萨烘培...");
    }

    @Override
    public void cut() {
        System.out.println("中式披萨的切片...");
    }

    @Override
    public void box() {
        System.out.println("中式披萨包装盒包装");
    }
}

Client

package com.penghua.factory;

public class Client {
    public static void main(String[] args) {
        Pizza chinaPizza = PizzaFactory.createPizza(1);

        System.out.println("=============================");
        Pizza americaPizza = PizzaFactory.createPizza(2);

        System.out.println("=============================");
        Pizza japanPizza = PizzaFactory.createPizza(3);

        System.out.println("=============================");
        Pizza otherPizza = PizzaFactory.createPizza(4);
    }
}
JapanPizza(日式披萨)
package com.penghua.factory;

class JapanPizza implements Pizza {

    @Override
    public void pre() {
        System.out.println("日式 披萨材料准备...");
    }

    @Override
    public void bake() {
        System.out.println("日式 披萨烘培...");
    }

    @Override
    public void cut() {
        System.out.println("日式 披萨的切片...");
    }

    @Override
    public void box() {
        System.out.println("日式 披萨包装盒包装");
    }
}

Pizza(父类:披萨)

package com.penghua.factory;

public interface Pizza {
    void pre();

    void bake();

    void cut();

    void box();
}
PizzaFactory(披萨工厂类)
package com.penghua.factory;

public class PizzaFactory {
    public static Pizza createPizza(int no){
        Pizza pizza = null;
        switch (no){
            case 1:
                pizza = new ChinesePizza();
                break;
            case 2:
                pizza = new AmericaPizza();
                break;
            case 3:
                pizza = new JapanPizza();
                break;
        }
        if (pizza == null){
            System.out.println("没有此披萨订购服务");
        }else {
            pizza.pre();
            pizza.bake();
            pizza.cut();
            pizza.box();
        }
        return pizza;
    }
}

代码其实差不多,不过把数据的初始化放到了工厂类中,不在各种披萨的构造方法了,这样的好处是可以保证后续(子类继承父类,不同的方法也不会影响),可以快速调整。

目的在于:实例化与初始化数据的解藕

前后的代码对比:

前者:将对象初始化的工作交给了对象的构造函数完成。

后者:将初始化的过程交给了工厂类完成。

这么做的好处在于解耦自身对象实例化和对象初始化动作,还有一个因素在于构造函数的初始化会影响到子类

注意:将对象的初始化交给工厂类,构造函数初始化会影响到子类,藕和度过高

Spring中的javabean是什么时候初始化的?

什么时候有值取决于bean的存在方式。

如果是单例模式那么在初始化spring上下文容器时就默认初始化了

如果是多列模式那么要在使用该对象时才会初始化

反正要初始化一次,干脆把所有的初始化操作放到监听器里面去,提高系统应用的性能
多例本身会耗性能,那么就尽可能在使用的时候再去创造对象
    

Bean的生命周期
        单例对象:
            出生:当容器创建时对象出生
            活着:只要容器还在,对象一直活着
            死亡:容器销毁,对象消亡
            总结:单例对象的生命周期和容器相同
        多例对象:
            出生:当我们使用对象时Spring框架为我们创建
            活着:对象只要是在使用过程中就一直活着
            死亡:当对象长时间不用,且没有别的对象引用时,由Java的垃圾回收器回收

结构性模式之适配器模式(Adapter):

Adapter 模式最大的作用还是将原本不兼容的接口融合在一起工作

其中相关的术语:

        source:待适配的类/对象/接口

        Adapter:适配器

        destination:适配后可用的类/对象/接口

举个例子:要让手机能够使用220V的交流电;注意,手机只接受5.5V的电压。

这个时候我们就会用到手机充电器来充电。这时他们的角色分配就是:

source Power220V===》220V的电源

Adapter PowerAdapt===》电源适配器(手机充电器)

Destination Power5V===》5V的电源

适配器之间也不是完全一样,有自己的分类:

类适配器模式: Adapter 类,通过继承 source 类,实现 Destination 类接口,完成 source->Destination 的适配。

对象适配器模式: 将 Adapter 类作修改,不是继承 source 类,而是持有 source 类的实例,以解决兼容性的问题。 即:持有 source 类,实现 Destination 类接口,完成source->Destination 的适配。

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

适配器模式分类三种体现形式思想都大同小异,这里就只介绍最常用的 对象适配器模式;

案例代码如下:

package com.penghua.Adapter;

public class Client {
    public static void main(String[] args) {
        Phone phone = new Phone();
//        已知有一个220V的电源,要用它给手机进行充电,我们只能将220V的电源进行处理后才能给手机充上电
//        还一种方案:新增5V的一个Voltage5V,Voltage的电压可以被手机使用
//        但是这违背现实生活现象,现实生活中只有220V的电源,其他的电源都是通过适配得来的
        phone.charge(new Voltage220V());
    }
}
package com.penghua.Adapter;


public class Phone {
//    充电
    public void charge(Voltage220V voltage220V){
        double voltage = voltage220V.getVoltage() / 40;
        System.out.println("最终手机充电所用电压:" + voltage + "V");
    }
}
package com.penghua.Adapter;

/**
 * 200v的电压
 */
public class Voltage220V {
    private double voltage;

    public Voltage220V() {
        this.voltage = 220;
    }

    public double getVoltage() {
        return voltage;
    }

    public void setVoltage(double voltage) {
        this.voltage = voltage;
    }
}

使用适配器后:

package com.penghua.Adapter;

public class Client {
    public static void main(String[] args) {
        Phone phone = new Phone();
//        已知有一个220V的电源,要用它给手机进行充电,我们只能将220V的电源进行处理后才能给手机充上电
//        VoltageAdapter适配器对Voltage220V这个不能直接使用的电源适配后就可以使用了
        phone.charge(new VoltageAdapter(new Voltage220V()));
    }
}
package com.penghua.Adapter;

public class Phone {
//    充电
    public void charge(Voltage5V voltage5V){
        double voltage = voltage5V.getVoltage();
        System.out.println("最终手机充电所用电压:" + voltage + "V");
    }
}
package com.penghua.Adapter;

/**
 * 目标接口
 */
interface Voltage5V{
    double getVoltage();
}
package com.penghua.Adapter;
/**
 * 220V的电压
 */
public class Voltage220V {
    private double voltage;

    public Voltage220V() {
        this.voltage = 220;
    }

    public double getVoltage() {
        return voltage;
    }

    public void setVoltage(double voltage) {
        this.voltage = voltage;
    }
}
package com.penghua.Adapter;

import com.penghua.Adapter.myAdapter.Voltage220V;

/**
 * 适配器:里面封装了source源到destination目标的过程
 */
class VoltageAdapter implements  Voltage5V {
    private Voltage220V voltage220V;

    public VoltageAdapter(Voltage220V voltage220V) {
        this.voltage220V = voltage220V;
    }

    public VoltageAdapter(com.penghua.Adapter.Voltage220V voltage220V) {

    }

    @Override
    public double getVoltage() {
        return voltage220V.getVoltage() / 40;
    }
}

桥接模式(Bridge):

技术术语:Bridge(桥接)

                  Abstraction(抽象类)

                  concrete:具体的

概念:桥接模式是将抽象部分与它的实现部分分离,使它们都可以独立地变化

案例

需求:手机(型号 + 品牌)操作问题;

完成手机各品牌各型号的项目设计;

列如:折叠式的华为、小米、Vivo,直立式的华为、小米、Vivo,旋转式的、滑盖的...

要求该项目设计易于扩展

首先我们们最容易想到的方案应该如下:

上图是常见的需求设计方案,非常不便于维护,手机的型号与品牌耦合度太高,当要新增滑盖(Slide)式的手机时,对应的品牌手机也要新增;  

接下来我们来看看桥接模式是如何设计的?

 桥接模式将分为两个模块,抽象部分(款式)和实现部分(品牌),抽象类(款式)中定义啦一个实现类(品牌),如Folded继承拉这个抽象类,并添加有参(Implement 品牌)的构造方法,这样品牌就不会受限制

代码:


public abstract class Abstraction {
    protected Implementor implementor;

    public abstract void call();
}

class Folded extends Abstraction {
    private String name = "折叠式";

    Folded(Implementor implementor) {
        this.implementor = implementor;
    }

    @Override
    public void call() {
        System.out.println(this.implementor.getName() + this.name + "正在通话中");
    }
}

class Upright extends Abstraction {
    private String name = "直立式";

    Upright(Implementor implementor) {
        this.implementor = implementor;
    }

    @Override
    public void call() {
        System.out.println(this.implementor.getName() + this.name + "正在通话中");
    }
}

class Slide extends Abstraction {
    private String name = "滑盖式";

    Slide(Implementor implementor) {
        this.implementor = implementor;
    }

    @Override
    public void call() {
        System.out.println(this.implementor.getName() + this.name + "正在通话中");
    }
}


package com.javaxl.design.bridge;

/**
 * @author 暗夜小白白
 * @site https://blog.youkuaiyun.com/qq_65975514?type=blog
 * @company
 * @create  2022年4月19日03:50:04
 * 手机品牌
 */
public interface Implementor {
    String getName();
}

class HW implements Implementor{
    private String name = "华为";

    @Override
    public String getName() {
        return name;
    }
}

class Mi implements Implementor{
    private String name = "小米";

    @Override
    public String getName() {
        return name;
    }
}

class Vivo implements Implementor{
    private String name = "Vivo";

    @Override
    public String getName() {
        return name;
    }
}


public class Client {
    public static void main(String[] args) {
        Folded folded = new Folded(new HW());
        folded.call();

        Upright upright = new Upright(new Mi());
        upright.call();

        Slide slide = new Slide(new Vivo());
        slide.call();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值