🌐 设计模式及源码分析(三)

363 阅读8分钟

四、结构型模式介绍

4.1 适配器模式(Adapter Pattern)

当我们需要在现有系统中使用一个类,但是这个类的接口与系统的要求不符合时,适配器模式就可以发挥作用。适配器模式通过引入一个中间件(适配器)来转换原始类的接口,使得原始类可以与系统进行适配。能够帮助解决不同接口之间的兼容性问题,提高系统的可维护性和可扩展性。

适配器模式的角色有:

  • 目标(Target)接口
  • 适配者(Adaptee)类
  • 适配器(Adapter)类

4.1.1 类结构型适配器

--------------------------------------场景模拟----------------------------------------

一个帅哥用华为手机看动物世界,一只小狗如何才能听懂华为手机的再讲什么呢,这时候就需要一个翻译功能,把人类的语言翻译成小狗的语言

----------------------------------------代码实现----------------------------------------

1.定义一个接口 Talk 、以及实现类 HuaWeiPhone (手机播放的汉语)

public interface Talk {

    String talk();
}

public class HuaWeiPhone implements Talk {
    @Override
    public String talk() {
        System.out.println("手机--小狗翻译");
        String content = "你在狗叫什么";
        System.out.println(content);  
        return content;
    }
}

2.定义一个接口 Translator、实现类 DogTalkTranslator (将汉语转换为狗语)

public interface Translator {
    String translate(String content);
}

public class DogTalkTranslator implements Translator{
    @Override
    public String translate(String content) {
        if("你在狗叫什么".equals(content)){
            return "汪汪汪汪汪汪";
        }
        if ("走开".equals(content)){
            return "汪汪";
        }
        return "--------";
    }
}

3.定义一个适配器类DogTalkAdapter

public class DogTalkAdapter extends DogTalkTranslator implements Talk {

    private Talk target;//被适配对象

    public DogTalkAdapter(Talk target){
        this.target = target;
    }

    @Override
    public String talk() {

        String talk = target.talk();
        //将汉语转化为狗语
        String translate = translate(talk);
        System.out.println("狗语:"+translate);
        return talk;
    }
}

4.测试类测试

public class MainTest {

   public static void main(String[] args) {

       HuaWeiPhone huaWeiPhone = new HuaWeiPhone();
       DogTalkAdapter dogTalkAdapter = new DogTalkAdapter(huaWeiPhone);

       huaWeiPhone.talk();
       dogTalkAdapter.talk();


   }
}

image.png

4.1.2 对象结构型适配器

1.定义一个适配器DogTalkAdapter (这里通过组合在对象的形式)

public class DogTalkAdapter implements Talk {

    //组合的方式
    private Translator translator = new DogTalkTranslator();
    private Talk target;//被适配对象
    public DogTalkAdapter(Talk target){
        this.target = target;
    }

    @Override
    public String talk() {

        String play = target.talk();
        //转换语言
        String translate = translator.translate(play);
        System.out.println("狗语:"+translate);
        return play;
    }
}

2.测试类测试

public class MainTest {
    public static void main(String[] args) {

        DogTalkAdapter adapter = new DogTalkAdapter(new HuaWeiPhone());
        adapter.talk();
    }

}

image.png

4.2 桥接模式(Bridge Pattern)

它将抽象部分与它的实现部分分离开来,使它们都可以独立地变化。这种模式就像一座桥,连接了两个不同的部分,让它们可以相互协作

--------------------------------------场景模拟----------------------------------------

假如有一个帅哥,他在德国和英国都有一栋房子,那么他可以选择骑自行车或者骑摩托车去,这时候的摩托车还有自行车就相当于一个连接方法,将帅哥和房子通过某种方式连接到一起

----------------------------------------代码实现----------------------------------------

1.定义 两个工具类 Bicycle 、 Motorcycle

public class Bicycle extends AbstractVehicle {
    @Override
    String getHome() {
        return "自行车:"+traffic.getTrafficInfo();
    }
}


public class Motorcycle extends AbstractVehicle {

    @Override
    String getHome() {
        return "摩托车:"+traffic.getTrafficInfo();
    }
}

2.定了两个目的地England 、Germany

public class England extends AbstractTraffic {
    public England(String speed, String time) {
        super(speed, time);
    }
}

public class Germany extends AbstractTraffic {
    public Germany(String speed, String time) {
        super(speed, time);
    }
}

3.定义一个抽象类AbstractTraffic (定义了车辆的速度和时间)

public abstract class AbstractTraffic {

    private String speed;
    private String time;
    public AbstractTraffic(String speed, String time){
        this.speed = speed;
        this.time = time;
    }

    String getTrafficInfo(){
        return "速度:"+ speed +"==>"+"时间:"+ time;
    }


}

4.定义一个抽象类AbstractVehicle (桥接的地方)

public abstract class AbstractVehicle {

    AbstractTraffic traffic; //分离渠道【桥接的关注点】
    abstract String getHome();

    public void setTraffic(AbstractTraffic traffic) {
        this.traffic = traffic;
    }
}

5.测试类测试

public class MainTest {

    public static void main(String[] args) {
        
        Motorcycle motorcycle = new Motorcycle();
        motorcycle.setTraffic(new Germany("180km/h","99分钟"));
        System.out.println(motorcycle.getHome());

        motorcycle.setTraffic(new England("180km/h","88分钟"));
        System.out.println(motorcycle.getHome());


        Bicycle bicycle = new Bicycle();
        bicycle.setTraffic(new Germany("160km/h","77分钟"));
        System.out.println(bicycle.getHome());

        bicycle.setTraffic(new England("160km/h","66分钟"));
        System.out.println(bicycle.getHome());


    }
}

image.png

4.3 装饰器模式(Decorator/Wrapper(包装) Pattern)

装饰模式就像是给一个物体穿上各种漂亮的外衣,让它看起来更独特😉 它是一种动态地给一个对象添加一些额外的职责的设计模式。

--------------------------------------场景模拟----------------------------------------

假如一个帅哥在一个直播平台,但是没有什么人观看,所有它就开了一个直播特效,把自己变得很神秘,最后吸引人来观看,这种特点就是装饰

----------------------------------------代码实现----------------------------------------

1.定义一个接口XiongMaoTv (直播平台)

public  interface XiongMaoTv {
   void show();
}

2.定义一个HandsomeBoyShow(帅哥没开特效直播)

public class HandsomeBoyShow implements XiongMaoTv {
    @Override
    public void show() {
        System.out.println("帅哥直播,没开直播特效:观众人数极少");
    }
}

3.定义一个装饰器接口XiongMaoTvDecorator (提供特效接口)

public interface XiongMaoTvDecorator extends XiongMaoTv {
    //开启特效
    void VFX();
}

4.定义一个装饰器实现类VFXDecorator (具体实现特效装饰功能)

public class VFXDecorator implements XiongMaoTvDecorator {


    private XiongMaoTv xiongMaoTv;
    public VFXDecorator(XiongMaoTv xiongMaoTv){
        this.xiongMaoTv = xiongMaoTv;
    }

    @Override
    public void show() {
        //开启特性
        VFX();
        //开始直播
        xiongMaoTv.show();
    }

    /**
     * 定义的增强功能
     */
    @Override
    public void VFX() {
        System.out.println("帅哥开启了直播特效:观众人数蹭蹭往上涨--");
    }
}

5.测试类测试

public class MainTest {

    public static void main(String[] args) {
        //没开直播特效
        XiongMaoTv xiongMaoTv = new HandsomeBoyShow();
        xiongMaoTv.show();
        System.out.println("------------------------");
        //开了直播特效
        VFXDecorator decorator = new VFXDecorator(xiongMaoTv);
        decorator.show();
    }
}

image.png

4.4 外观模式(Facade Pattern)

外观模式的主要思想是将系统的复杂部分隐藏在一个外观对象后面,这个外观对象提供了一个简化的接口,用户只需与这个接口进行交互,而无需了解系统内部的复杂结构和操作。这样可以提高系统的易用性和可维护性。

--------------------------------------场景模拟----------------------------------------

有一个帅哥想去理发,又想去打台球,又想去吃饭,但是这三个地方都相隔很远,纠结要不要去,这时候帅哥看到一则消息,有一个地方同时满足了他这三个要求,他就果断去了

----------------------------------------代码实现----------------------------------------

1.定义Canteen、GoodPlace、Barbershop (吃饭,理发,打台球)

public class Canteen {

    public void eat(String name){
        System.out.println(name+"吃饭-------");
    }
}

public class Barbershop {

    public void action(String name){
        System.out.println(name + "理发-----");
    }
}

public class BilliardParlor {

    public void play(String name){
        System.out.println(name+"打台球-------");
    }
}

2.定义一个门面类GoodPlace (同时满足三种需求)

public class GoodPlace {

    Barbershop barbershop = new Barbershop();
    Canteen canteen = new Canteen();
    BilliardParlor billiardParlor = new BilliardParlor();
    
    public void handle(String name){
        barbershop.action(name);
        canteen.eat(name);
        billiardParlor.play(name);
    }
}

3.测试类测试

public class MainTest {
    public static void main(String[] args) {

        GoodPlace goodPlace = new GoodPlace();
        goodPlace.handle("帅哥");
    }
}

image.png

4.5 组合模式(Composite Pattern)

允许你将对象组合成树状结构,并以一致的方式处理单个对象和组合对象。

--------------------------------------场景模拟----------------------------------------

就是类似一些电商网站中,一些菜单的树形结构

image.png

这时候三级菜单就组合在二级菜单里面,二级菜单又组合在一级菜单里面

----------------------------------------代码实现----------------------------------------

1.定义 Menu(菜单的各种属性)

@Data
public class Menu {

    private Integer id;
    private String menuName;
    public Menu(Integer id,String menuName){
        this.id = id;
        this.menuName = menuName;
    }

    //组合模式关注点
    private List<Menu> childs = new ArrayList<>();

    //提供添加层级的方法
    void addChildMenu(Menu menu){
        childs.add(menu);
    }

    //层级遍历方法
    void printMenu(){
        System.out.println(menuName);
        if(childs.size() > 0){
            for (Menu child : childs) {
                child.printMenu();
            }
        }
    }

}

2.测试类测试

public class MainTest {
    public static void main(String[] args) {
        Menu menu = new Menu(1, "电商树形结构");
        Menu one = new Menu(2, "一级分类:手机");
        menu.addChildMenu(one);
        one.addChildMenu(new Menu(6,"二级分类:手机通讯"));
        one.addChildMenu(new Menu(7,"二级分类:手机配件"));
        
        Menu two = new Menu(3, "一级分类:电脑");
        menu.addChildMenu(two);
        two.addChildMenu(new Menu(4,"二级分类:电脑配件"));
        two.addChildMenu(new Menu(5,"二级分类:电脑整机"));

        menu.printMenu();
    }
}

image.png

4.6 享元模式(Flyweight Pattern)

一种优化性能的设计模式,它用于减少对象的创建数量,从而节省内存和提高系统性能。 享元模式的核心思想是将具有相同内部状态的对象共享使用。通过创建一个共享对象池,当需要创建对象时,首先检查池中是否已经存在具有相同状态的对象,如果存在则直接返回这个对象,而不是创建新的对象。

享元模式中的角色

  • Flyweight: 抽象享元类
  • ConcreteFlyweight: 具体享元类
  • UnsharedConcreteFlyweight: 非共享具体享元类
  • FlyweightFactory: 享元工厂类;简单工厂

--------------------------------------场景模拟----------------------------------------

比如在一些数据库连接池中,我们要频繁的连接数据库,这时候如果每次连接数据库都开启一个数据库连接,就会很浪费性能,我们可以把数据库连接这个功能放入数据池中,谁需要就去调用即可

----------------------------------------代码实现----------------------------------------

1.定义一个数据连接工具类JDBCConnect

@AllArgsConstructor
public class JDBCConnect extends AbstractWaitressFlyweight {

    String connectId;
    String connectName;
    
    @Override
    void begin() {
        System.out.println("数据连接id:"+connectId+";"+ connectName +" 开始连接连接数据库");
        //改变外部状态
        this.canService = false;
    }

    @Override
    void end() {
        System.out.println("数据连接id:"+connectId+";"+ connectName +"关闭数据库连接");

        this.canService = true;
    }
}

2.定义一个数据连接池DatabaseConnectionPool

public class DatabaseConnectionPool {

    private static Map<String,AbstractWaitressFlyweight> pool = new HashMap<>();
    //享元,池子中有对象
    static {
       JDBCConnect waitress =
                new JDBCConnect("10086","数据连接10086号");
        JDBCConnect waitress2 =
                new JDBCConnect("12345","数据连接12345号");

        pool.put(waitress.connectId,waitress);
        pool.put(waitress2.connectId,waitress2);
    }

    public static AbstractWaitressFlyweight getWaitress(String name){
        AbstractWaitressFlyweight flyweight = pool.get(name);
        if(flyweight == null){
            for (AbstractWaitressFlyweight value : pool.values()) {
                //当前共享对象能否是否
                if(value.isCanService()){
                    return value;
                }
            };
            return null;
        }
        return flyweight;

    }

}

3.定义一个抽象类AbstractWaitressFlyweight

public abstract class AbstractWaitressFlyweight {

    boolean canService = true;//能否连接

    //正在服务。   享元的不可共享属性留给外部进行改变的接口
    abstract void begin();
    //服务完成。   享元的不可共享属性留给外部进行改变的接口
    abstract void end();

    public boolean isCanService() {
        return canService;
    }
}

4.测试类测试

public class MainTest {

    public static void main(String[] args) {

        AbstractWaitressFlyweight abstractWaitressFlyweight = DatabaseConnectionPool.getWaitress("");
        abstractWaitressFlyweight.begin();
        System.out.println(abstractWaitressFlyweight);
        AbstractWaitressFlyweight ab = DatabaseConnectionPool.getWaitress("");
        ab.begin();
        System.out.println(ab);

        ab.end();
        AbstractWaitressFlyweight aw = DatabaseConnectionPool.getWaitress("");
        System.out.println(aw);
    }
}

image.png

特别的需要注意的是享元模式与单例模式的区别就在于,我们那到的实例,享元模式是他的本体,而单例模式拿到的是克隆体