当前位置:   article > 正文

设计模式之接口型设计模式_接口设计

接口设计

接口型设计模式

  • 适配器模式(Adapter)

    将没有耦合的两段程序通过适配器使它们相互协作

  • 外观模式(Facade)

    为防止程序变成混乱在一团的小块程序,将它们归类,引用一个外观角色来降低外部的使用复杂度

  • 合成模式(Composite)

    可嵌套的对象组合,更加方便的处理对象们的聚合关系

  • 桥接模式(Bridge)

    抽象与实现的分离,可自由组合,代替抽象与实现两个可变维度的多种变化

前言

接口类是一种约束,一个类(实现了该接口的类)的特征集,而这种特征集被java抽象的定义为接口,并允许它真实的独立存在,且将接口与实现分离,且每个类都能使用,也不限于某一个接口,而是一个类可以实现多个接口。这种约束可以让我更加清晰的阅读程序代码,当我们看到一个实现类后,我们迅速的知道该实现类会有那些动作,例如:老虎,在我们了解到老虎属于猫科动物时,首先下意识的明白它是有尖尖的爪子、细长的牙齿,但是猫科动物们具体怎么使用爪子和牙齿却没有限制。

接口其实也可以不带方法,此类型的接口被称之为标记型接口,例如Cloneable

/*
 * Copyright (c) 1995, 2004, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package java.lang;

/**
 * A class implements the <code>Cloneable</code> interface to
 * indicate to the {@link java.lang.Object#clone()} method that it
 * is legal for that method to make a
 * field-for-field copy of instances of that class.
 * <p>
 * Invoking Object's clone method on an instance that does not implement the
 * <code>Cloneable</code> interface results in the exception
 * <code>CloneNotSupportedException</code> being thrown.
 * <p>
 * By convention, classes that implement this interface should override
 * <tt>Object.clone</tt> (which is protected) with a public method.
 * See {@link java.lang.Object#clone()} for details on overriding this
 * method.
 * <p>
 * Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
 * Therefore, it is not possible to clone an object merely by virtue of the
 * fact that it implements this interface.  Even if the clone method is invoked
 * reflectively, there is no guarantee that it will succeed.
 *
 * @author  unascribed
 * @see     java.lang.CloneNotSupportedException
 * @see     java.lang.Object#clone()
 * @since   JDK1.0
 */
public interface Cloneable {
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

许多设计模式都是使用了Java内建的这种特性。例如,适配器(Adapter)模式,通过使用一个接口类型来适配类的接口,从而达到想要的目的。若想运用好java基本的内建特性,就要从接口开始,确保自己掌握了Java特性的工作原理。

接口与抽象类

很多语言都不存在接口的这种结构,它们只有抽象类而没有接口,例如C++。由于Java接口与抽象类非常相似,如果不使用接口,也完全可以像C++那样使用抽象类,然而,作为一种独立的机构,接口在多层的应用程序开发过程中的地位举足轻重。

抽象类与接口的区别
  • 抽象类只能被单继承;接口可以被多实现(接口也可以继承接口,并且可以继承多个接口)

  • 抽象类可以用具体方法;接口的所有方法都是抽象方法

  • 抽象类可以声明字段和使用字段;接口则不能,但是可以创建静态默认修饰或者final默认修饰常量(在java1.5后,引进了enum,逐渐被enum所替代)

  • 抽象类的方法可以使public、protected、private或者默认的package;接口里的方法都是public

  • 抽象类可以定义构造函数;接口则不能

切记

不同于方法,方法可以默认public关键字修饰,但接口如果省略掉public,那么该接口只能在同级包下使用

注意,该接口是没有public关键字的

package org.learn.patterns.interfaces;

interface InterfaceDemo {
}
  • 1
  • 2
  • 3
  • 4

在不同的包下,我们无法使用这个接口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iAo3GXH1-1672987154123)(Java设计模式/pic/image-20220703101421445.png)]

有时候,一些接口中的某些方法我们并不是都需要,但是Java接口的实现却是规范全部重写(override),为了在这一特性下灵活的使用接口,我们可以定义一些桩(stub),即可以提供一个空实现的接口实现类(注意这个类,不是接口),当我们再想用这些方法时,只需继承它的实现类即可,我们可以从实现类的方法中选择那些更有效的方法来实现,而忽略那些不关心的方法

原接口

我们假设猫科动物接口有这四类特征,我们如果要定义一个老虎子的实现类,目前我们只认为老虎会使用牙齿和爪子进行撕咬。其它的,例如舌头和尾巴,老虎很少使用,所以我们只需要实现useClaws()和useTooth()两个方法,但是Java的接口规范是必须要实现所有方法。

package org.learn.patterns.interfaces;

public interface Felid {
    public void useClaws();
    public void useTooth();
    public void useTongue();
    public void useTail();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

桩(stub)就很好的解决了这一点,它让接口变得像普通类一样,子类可以选择性的继承一些自己关注的方法。

package org.learn.patterns.implementss;

import org.learn.patterns.interfaces.Felid;

public class FelidStub implements Felid {
    @Override
    public void useClaws() {}

    @Override
    public void useTooth() {}

    @Override
    public void useTongue() {}

    @Override
    public void useTail() {}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

资源类

​ 我们新建的老虎类并不用在实现猫科动物接口的所有方法,我们直接继承桩,然后选择自己需要的方法再重写。

package org.learn.patterns.implementss;

public class Tiger extends FelidStub {
    @Override
    public void useClaws() {
        System.out.println("老虎使用了一次爪子");
    }

    @Override
    public void useTooth() {
        System.out.println("老虎使用了一次牙齿");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

适配器模式(Adapter)

适配器模式是指,把一个现有类的方法包装进另一个接口的实现类的方法里进行调用,使两个没有任何关系的类可以相互协作。适配器模式的中心思想还是让两段没有任何耦合代码可以通过适配器类进行相互协作,适配器模式可分为对象适配器类适配器两种。

概念角色

  • Target(目标抽象类或接口):抽象定义客户端所需要的接口,即暴露给客户端使用的接口,可以是接口,也可以是抽象类,也可以是具体类

  • Adapter(适配器):适配器类,具体的实现类,通过继承(或实现)Target并关联Adaptee类使二者进行联动。

  • Adaptee(被适配者):被适配者,已经存在的接口实现方案,一般情况都是一个具体类,但是需要进行适配。

类适配器

类适配器通常将已有的逻辑适配进程序中,这样即保留了原有代码,已达到了目的。如果我们不使用适配器模式,我们需要将被适配者的逻辑全部复制到目标抽象类(或接口)的实现中,这样重复造轮子实在有点小题大做。类适配器通常要满足里氏代换原则,才能完全适配使用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DF1q3Bba-1672987154124)(Java设计模式/pic/image-20220715145333893.png)]

Target

此接口为暴露给客户端的接口

package org.learn.patterns.interfaces.adapter_patterns;

public interface Target {
    public void method1();
}
  • 1
  • 2
  • 3
  • 4
  • 5

Adaptee

Adaptee类通常是真实开发的代码,通常会与原接口存在差异,所以需要适配

package org.learn.patterns.interfaces.adapter_patterns;

public class AdapteE {
    public void adapteeMethod(){
        System.out.println("这是被适配者的方法");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Adapter

Adapter的作用就是将AdapteE的逻辑适配进来

package org.learn.patterns.interfaces.adapter_patterns;

public class Adapter extends AdapteE implements Target {
    @Override
    public void method1() {
        adapteeMethod();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Test

public static void main(String[] args) {
        Adapter adapter = new Adapter();
        adapter.method1();
    }
  • 1
  • 2
  • 3
  • 4

执行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XfRoilUr-1672987154125)(Java设计模式/pic/image-20220705151007373.png)]

对象适配器

很多时候需要适配的方法并非抽象,因此类适配器的方式就不奏效了。对象适配器弃用继承而使用委派(方法调用),这种方式虽然更加灵活,但是却也更加脆弱。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jxvvkJTl-1672987154125)(Java设计模式/pic/image-20220715163503152.png)]

旧的车(target)

package org.learn.patterns.interfaces.adapter_patterns.object_adapter;

public class OldCar {
    protected String carKay = "CarKay";
    private int tyre;
    private String frame;

    public OldCar(int tyre, String frame) {
        this.tyre = tyre;
        this.frame = frame;
    }

    public void run() {

    }

    public void stop() {

    }

    public int getTyre() {
        return tyre;
    }

    public String getFrame() {
        return frame;
    }


}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

新能源汽车(Adaptee)

package org.learn.patterns.interfaces.adapter_patterns.object_adapter;

public class NewEnergyCar {
    private double electric;
    private int driver;

    public NewEnergyCar(double electric, int driver) {
        this.electric = electric;
        this.driver = driver;
    }
    public String startCar(String carKey){
        return "新能源汽车启动了";

    }
    public String breakCar(String carKey){
        return "新能源汽车停车了";
    }

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

适配器类

package org.learn.patterns.interfaces.adapter_patterns.object_adapter;

public class GoodCar extends OldCar {
    private NewEnergyCar newEnergyCar;

    public GoodCar(int tyre, String frame, NewEnergyCar newCar) {
        super(tyre, frame);
        newEnergyCar = newCar;
    }

    @Override
    public void run() {
        System.out.println(getFrame() + getTyre() + "个轮胎的" + newEnergyCar.startCar(carKay));
    }

    @Override
    public void stop() {
        System.out.println(getFrame() + getTyre() + "个轮胎的" + newEnergyCar.breakCar(carKay));

    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
缺点

对象适配器相较于类适配器有着非常大的风险

  • NewEnergyCar类的适配方法必须修饰为final,否则子类继承并重写该方法会带来较大的不稳定性

  • OldCar类并不是接口,且我们无法约束它的变化,可能会在运行时出现编译期无法检测到的问题

外观模式(Facade)

面向对象的最大的优势,在于它能够防止应用程序变成混乱纠缠在一团的多个小块程序。而外观模式通常把这一优势发挥得淋漓尽致,它通过引入一个外观角色来简化客户端子系统之间的交互,即为复杂的子系统提供统一的入口,降低客户端子系统的耦合性,尽可能地将可复用的模块组织在一起。

假设我们去购买了一杯奶茶,对于奶茶店而言,制作一杯奶茶通常分为加小料、加茶水、加糖、加冰块、搅拌、封口、打包,然而这些步骤通常已经被奶茶店所“封装”,站在我们的角度,我们只需要做的是买单然后喝奶茶就行,这个例子也就是现实生活的中的“外观模式”,我们点单的操作其实就相当于在使用外观类,而制作奶茶的这些“动作”就是奶茶的一些“子系统”,本质上我们其实还是在使用子系统,但是有了外观模式的介入后,我们可以更加轻便地使用这些子系统。从而也满足了面向对象编程系统的特性,即“不关心过程与实现,只关心结果”。

概念角色

  • **外观类(Facade):**外观类通常会暴露给客户端使用,外观类必须要了解子系统的职责和功能,通常情况下,外观类从客户端收到“命令”后,将“任务”拆分成各个职责委派给各个子系统。

  • **子系统(SubSystem):**子系统就是整个外观模式的真实“打工仔”,所有工作最终还是要靠子系统完成。子系统们其实感受不到外观类的存在,它们也可以被客户端直接调用,对于它们而言,外观类也仅仅客户端而已。

示例:家庭影院

这个例子是在网上抄的,但是,我只抄了它的代码例子,其它的东西一概不碰,因为我是实在想不到比家庭影院更合适的例子了,所以请原谅我的“借鉴”。

原文链接:https://blog.csdn.net/qq_45034708/article/details/114972361*/
  • 1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6tOvD8Eh-1672987154125)(Java设计模式/pic/image-20221213165349556.png)]

子系统

播放器 保留原文链接,这是对作者的尊重

package org.learn.patterns.facede_patterns;

public class Player {
    private static Player instance = new Player();

    private Player() {
    }

    public static Player getInstance() {
        return instance;
    }

    public void on() {
        System.out.println(" 播放器打开了 ");
    }

    public void off() {
        System.out.println(" 播放器关闭了 ");
    }

    public void play() {
        System.out.println(" 播放器播放中 ");
    }

    public void pause() {
        System.out.println(" 播放暂停 ");
    }
    /*————————————————
    版权声明:本文为CSDN博主「吾仄lo咚锵」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qq_45034708/article/details/114972361*/
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

投影仪

package org.learn.patterns.facede_patterns;

public class Projector {
    private static Projector instance = new Projector();

    private Projector() {
    }

    public static Projector getInstance() {
        return instance;
    }

    public void on() {
        System.out.println(" 投影仪打开了 ");
    }

    public void off() {
        System.out.println(" 投影仪关闭了 ");
    }

    public void focus() {
        System.out.println(" 投影仪聚焦 ");
    }

    public void zoom() {
        System.out.println(" 投影仪放大 ");
    }
    /*————————————————
    版权声明:本文为CSDN博主「吾仄lo咚锵」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qq_45034708/article/details/114972361*/
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

屏幕

package org.learn.patterns.facede_patterns;

public class Screen {
    private static Screen instance = new Screen();

    private Screen() {
    }

    public static Screen getInstance() {
        return instance;
    }

    public void up() {
        System.out.println(" 屏幕上升 ");
    }

    public void down() {
        System.out.println(" 屏幕下降 ");
    }
    /*————————————————
    版权声明:本文为CSDN博主「吾仄lo咚锵」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qq_45034708/article/details/114972361*/
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

音响

package org.learn.patterns.facede_patterns;

public class Stereo {
    private static Stereo instance = new Stereo();

    private Stereo() {
    }

    public static Stereo getInstance() {
        return instance;
    }

    public void on() {
        System.out.println(" 音响打开了 ");
    }

    public void off() {
        System.out.println(" 音响关闭了 ");
    }

    public void setVolume() {
        System.out.println(" 音响音量调节 ");
    }
    /*————————————————
    版权声明:本文为CSDN博主「吾仄lo咚锵」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qq_45034708/article/details/114972361*/
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
外观类

家庭影院类

package org.learn.patterns.facede_patterns;

public class FamilyCinema {
    private Player player;
    private Screen screen;
    private Projector projector;
    private Stereo stereo;

    public FamilyCinema() {
        this.player = Player.getInstance();
        this.screen = Screen.getInstance();
        this.projector = Projector.getInstance();
        this.stereo = Stereo.getInstance();
    }
    public void ready(){
        System.out.println("准备工作开始==========");
        screen.down();
        projector.on();
        stereo.on();
        player.on();
        stereo.setVolume();
        projector.on();
        projector.zoom();
        projector.focus();
    }
    public void start(){
        System.out.println("电影开始==========");
        player.play();
    }
    public void pause() {
        System.out.println("电影暂停==========");
        player.pause();
    }
    public void end(){
        System.out.println("电影播放完毕==========");
        player.off();
        stereo.off();
        screen.up();
        projector.off();
    }
 /*————————————————
    版权声明:本文为CSDN博主「吾仄lo咚锵」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qq_45034708/article/details/114972361*/
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

合成模式(Composite)

合成模式,也称组合模式。合成模式是一组对象的组合,像是一把折扇,每个对象像是折扇的扇骨,折扇可以轻松合上与打开,因为它的扇骨都是同一大小且相同形状,合上以后成扑克牌状,从上面看就像是只有一张扑克牌一样。合成模式也是利用这一点,所以对象必须实现同一抽象,这样才能形成聚合关系,但是又优于扇子结构。扇子只有一个这个整体结构(扇子本身)才能算是“容器”,扇骨只能算是“节点”,扇骨无法在包含其它扇骨。而合成模式的"扇骨"却分为两种,一种是Composite(节点),俗称“树枝”,另一种是Leaf,俗称“叶子”,不同于普通节点“叶子”,“树枝”可以作为容器包含其它“叶子”与“树枝”,从而形成递归关系,可以处理更加复杂的组合关系。

概念角色

  • Component(抽象组件):“叶子”与“树枝”必须实现(或继承)的抽象类。该组件定义了管理子节点的抽象方法。
  • Leaf(叶子):“叶子”没有子节点,对于父类的管理管理子节点的方法,通常会以抛异常的方式处理。
  • Composite(树枝):“树枝”可以包含子节点,可以包含除Component以外所有的子节点。

示例:家族关系

现在我们把外公家的家庭成员用合成模式表示出来

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cACm2sWq-1672987154126)(Java设计模式/pic/image-20221224091341345.png)]

抽象组件

​ 用抽象类是为了保留它固定的属性,例如:姓名,使它的任意一级子类都能轻松使用

package org.learn.patterns.composite_patterns;

import java.util.ArrayList;
import java.util.List;

public abstract class Kinship {
   //他们小孩
   protected List<Kinship> offspring = new ArrayList<>();
   
   //称呼,例如:大舅、大姨
   protected String name;

   public Kinship(String name) {
       this.name = name;
   }
   //声明是他的孩子,例如大表姐是大舅家的小孩
   public abstract void hisChild(Kinship people);
   
   //结婚,互相“连接”的配偶关系
   public abstract void marry(Kinship people);
   
   //子女离家出走,断开关系,类似删除方法
   public abstract void leave(Kinship people);
   
   //get方法
   public abstract Kinship getChild(int idx);

   //通知方法。相互转告
   public abstract void tell(String message);

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
树枝

成年人(类),成年人可以拥有小孩,所以这是树枝类,为了更符合逻辑,我又多加了一个配偶对象

package org.learn.patterns.composite_patterns;


import java.util.List;
import java.util.Objects;

public class Adult extends Kinship {
	//配偶
    private Kinship spouse;

    public Adult(String name) {
        super(name);
    }

  
    @Override
    public void hisChild(Kinship people) {

        offspring.add(people);
        if (Objects.nonNull(spouse)) {
            if (!spouse.offspring.contains(people)) {
                System.out.println(name + "与" + spouse.name + "的孩子是:" + people.name);
                spouse.hisChild(people);
            }

        }
    }

    @Override
    public void marry(Kinship spouse) {
        if (Objects.nonNull(spouse)) {
            if (!spouse.equals(this.spouse)) {
                this.spouse = spouse;
                spouse.marry(this);
            }

        }

    }

    @Override
    public void leave(Kinship people) {
        offspring.remove(people);
    }

    @Override
    public Kinship getChild(int idx) {
        return offspring.get(idx);
    }

    @Override
    public void tell(String message) {
        System.out.println(name + "家========");
        System.out.println(name + "已经下发消息:" + message);
        System.out.println(name + "已经收到消息:" + message);
        if (Objects.nonNull(this.spouse)) {
            System.out.println(this.spouse.name + "已经收到消息:" + message);
        }
        offspring.forEach(people -> people.tell(message));

    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
叶子

小孩(类),叶子节点,小孩没有小孩。

package org.learn.patterns.composite_patterns;


public class Kid extends Kinship {

    public Kid(String name) {
        super(name);
    }

    @Override
    public final void hisChild(Kinship people) {
        System.out.println("小孩不能生小孩");
    }

    @Override
    public final void marry(Kinship people) {
        System.out.println("小孩不能结婚");
    }

    @Override
    public final void leave(Kinship people) {
        System.out.println("小孩不能管别人离家出走");
    }

    @Override
    public final Kinship getChild(int idx) {
        System.out.println("小孩没有小孩");
        return null;
    }

    @Override
    public void tell(String message) {
        System.out.println(name + "已经收到消息:" + message);
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
start
package org.learn.patterns.composite_patterns;

public class Main {
    public static void main(String[] args) {
        //外公家====================================
        Kinship grandfather = new Adult("外公");

        Kinship djUncle = new Adult("大舅");
        Kinship dyAunt = new Adult("大姨");
        Kinship mom = new Adult("妈妈");
        Kinship xjUncle = new Adult("小舅");
        Kinship xyAunt = new Adult("小姨");


        //大舅家====================================
        Kinship djmAunt = new Adult("大舅妈");
        Kinship dbjFemaleCousin = new Kid("大表姐");
        Kinship xbjFemaleCousin = new Kid("小表姐");

        djUncle.marry((Adult) djmAunt);
        djUncle.hisChild(dbjFemaleCousin);
        djUncle.hisChild(xbjFemaleCousin);


        //大姨妈家====================================
        Kinship dyfUncle = new Adult("大姨夫");
        Kinship dbmFemaleCousin = new Kid("大表妹");
        Kinship xbmFemaleCousin = new Kid("小表妹");
        Kinship bdMaleCousin = new Kid("表弟");

        dyAunt.marry((Adult) dyfUncle);
        dyAunt.hisChild(dbmFemaleCousin);
        dyAunt.hisChild(xbmFemaleCousin);
        dyAunt.hisChild(bdMaleCousin);


        //妈妈家====================================
        Kinship dad = new Adult("爸爸");
        Kinship elderSister = new Kid("姐姐");
        Kinship youngerBrother = new Kid("弟弟");

        mom.marry((Adult) dad);
        mom.hisChild(elderSister);
        mom.hisChild(youngerBrother);


        //小舅家====================================
        Kinship xjmAunt = new Adult("小舅妈");
        Kinship bmFemaleCousin = new Kid("表妹");

        xjUncle.marry((Adult) xjmAunt);
        xjUncle.hisChild(bmFemaleCousin);


        //小姨妈家====================================
        Kinship xyfUncle = new Adult("小姨夫");
        Kinship femaleCousin = new Kid("表姐");
        Kinship xbdMaleCousin = new Kid("小表弟");

        xyAunt.marry((Adult) xyfUncle);
        xyAunt.hisChild(femaleCousin);
        xyAunt.hisChild(xbdMaleCousin);

        //============================================
        grandfather.hisChild(djUncle);
        grandfather.hisChild(dyAunt);
        grandfather.hisChild(mom);
        grandfather.hisChild(xyAunt);

        //外公发话了====================================
        grandfather.tell("过来吃饭");

    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
执行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oQcTeZwh-1672987154126)(Java设计模式/pic/image-20220721104800029.png)]

桥接模式(Bridge)

将抽象与实现分离,使它们可以自由组合成多种形态,来代替抽象与实现两个可变维度的多种变化,这里的抽象是指一个对象的主要组成部分(例如玩具的样式,汽车、洋娃娃、积木),实现是指一个对象的次要组成部分(例如玩具的颜色,红色、蓝色、绿色),我个人把这其中的抽象理解主要抽象,把实现理解为另外一个次要抽象,而桥接模式就是分离出这两种抽象并降低它的耦合而又复用它们的子类形成多种组合从而代替抽象的多种变化。假设主次抽象不分离,那么我们要展示所有9个由不同颜色和种类组成的玩具就要定义9个该抽象的子类,如果再多出一种颜色与玩具种类,那么该抽象的子类又会增加到16个。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NtKdZdNE-1672987154127)(Java设计模式/pic/image-20220725113925742.png)]

所以主次抽离分离可以很好的避免这一点,我们需要将它的颜色单独抽离成另外一个抽象(接口),然后将次要抽象的引用交给主要抽象封装,复用主次抽象的种类与颜色子类可组合成多种不一样的玩具,正是有了这两种抽象的分离才能防止组合数量有几何数增长。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BfB0TRUc-1672987154127)(Java设计模式/pic/image-20220725151401205.png)]

概念角色

  • 抽象类:主要抽象,作为最后实现的主要属性,它一定得设计成抽象类,这样才能封装次要抽象让它的子类”桥接“

  • 具体类:主要抽象的拓展子类,因为父类封装了次要抽象,所以它可以直接调用次要抽象的方法

  • 次要抽象类:分离出来的次要抽象接口

  • 次要具体类:次要抽象接口的拓展子类

示例:玩具生产

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HU5Q3XxC-1672987154127)(Java设计模式/pic/image-20230106103121218.png)]

抽象类:玩具

package org.learn.patterns.bridge_patterns;

public abstract class Toys {
    protected Color color;//次要抽象对象的引用
	//增加构造方法使其子类必须有颜色
    public Toys(Color color) {
        this.color = color;
    }
	//制作玩具
    public abstract void make();
    //改变玩具颜色
    public void setColor( Color color){
        this.color=color;
    };
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

具体类:玩具种类-洋娃娃

package org.learn.patterns.bridge_patterns;

public class Doll extends Toys {
    public Doll(Color color) {
        super(color);
    }

    @Override
    public void make() {
        System.out.println("生产了一个洋娃娃");
        color.paint();
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

次要抽象:颜色

package org.learn.patterns.bridge_patterns;

public interface Color {
    //涂上颜色
    void paint();
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

次要具体类:颜色-红色

package org.learn.patterns.bridge_patterns;

public class Red implements Color {
    @Override
    public void paint() {
        System.out.println("涂上了红色");
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

PS:其它颜色与种类就不写了

start

package org.learn.patterns.bridge_patterns;

public class Main {
    public static void main(String[] args) {
        //红色汽车
        Toys car=new Car(new Red());
        car.make();
		//绿色洋娃娃
        Toys doll=new Doll(new Green());
        doll.make();
		//蓝色积木
        Toys bricks=new Bricks(new Blue());
        bricks.make();


    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

执行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LmtwlF2j-1672987154128)(Java设计模式/pic/image-20220726155256685.png)]

示例拓展:三个及以上维度的桥接模式

我在浏览技术大牛的博客时,看见底下评论了这么一句话:“如果再加一个属性类是不是还得改主要抽象类?这么做是不是违背了开闭原则?跟直接写一个属性有什么区别呢?”。然而也有人说桥接模式只能适用两个维度的桥接,如果有三维度或以上那只能是使用别的设计模式了,为此我特别修改了一下,使上面的桥接模式能够适应三个及以上的属性。

主要抽象类:玩具

package org.learn.patterns.interfacial.bridge_patterns.hight_level;

public abstract class Toys {
    //为了适应多个属性,次要抽象已经变成一个“属性工厂”
    protected AbstractAttrsFactory attributesFactory;

    public Toys(AbstractAttrsFactory attributesFactory) {
        this.attributesFactory = attributesFactory;
    }
    //制作玩具
    public abstract void make();
    //重新设置属性
    public void setAttributesFactory(AbstractAttrsFactory attributesFactory) {
        this.attributesFactory = attributesFactory;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

具体类:玩具种类-洋娃娃

package org.learn.patterns.interfacial.bridge_patterns.hight_level;

public class Doll extends Toys {
    public Doll(AbstractAttrsFactory attributesFactory) {
        super(attributesFactory);
    }

    @Override
    public void make() {
        System.out.println("生产了一个高级娃娃");
        //添加属性
        attributesFactory.transfer();
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

次要抽象:抽象属性工厂

package org.learn.patterns.interfacial.bridge_patterns.hight_level;



public abstract class AbstractAttrsFactory {
    //属性对象数组
    protected Attributes[] attributes;
    //使用可变参数作为传参可以使添加属性变灵活
    public AbstractAttrsFactory(Attributes... attributes) {
        this.attributes = attributes.clone();
    }
    //此方法统一调用所有属性对象的方法
    public abstract void transfer();
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

次要实体类:属性工厂

package org.learn.patterns.interfacial.bridge_patterns.hight_level;

public class AttributesFactory extends AbstractAttrsFactory {


    public AttributesFactory(Attributes... attributes) {
        super(attributes);
    }

    @Override
    public void transfer() {
        //循环遍历调用所以属性对象里的方法
        for (int i = 0; i < attributes.length; i++) {
            attributes[i].execute();
        }


    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

次要抽象:属性接口

package org.learn.patterns.interfacial.bridge_patterns.hight_level;

public interface Attributes {
    void execute();
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

次要标识接口:颜色

package org.learn.patterns.interfacial.bridge_patterns.hight_level;

public interface Color extends Attributes {
}

  • 1
  • 2
  • 3
  • 4
  • 5

次要具体类:颜色-红色

package org.learn.patterns.interfacial.bridge_patterns.hight_level;

public class Red implements Color {
    @Override
    public void execute() {
        System.out.println("涂上红色");
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

次要标识接口:尺寸

package org.learn.patterns.interfacial.bridge_patterns.hight_level;

public interface Size extends Attributes{
}

  • 1
  • 2
  • 3
  • 4
  • 5

次要具体类:尺寸-L

package org.learn.patterns.interfacial.bridge_patterns.hight_level;

public class LargeSize implements Size {
    @Override
    public void execute() {
        System.out.println("尺寸是L");
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

start

package org.learn.patterns.interfacial.bridge_patterns.hight_level;

public class HLMain {
    public static void main(String[] args) {
        Toys toy=new Doll(new AttributesFactory(new Red(),new LargeSize()));
        toy.make();
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

执行结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BKIynhI7-1672988482456)(Java设计模式/pic/image-20230106150048259.png)]

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号