Leetcode-设计模式-结构型模式

Posted 钢铁-程序猿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Leetcode-设计模式-结构型模式相关的知识,希望对你有一定的参考价值。

文章目录

Leetcode-设计模式-结构型模式

一、适配器模式

适配器模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
主要解决:主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。

何时使用:

  • 1、系统需要使用现有的类,而此类的接口不符合系统的需要。
  • 2、想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。
  • 3、通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)

如何解决:继承或依赖(推荐)。

关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。

菜鸟教程适配器模式

二、桥接模式(将两个独立的结构联系起来,两个被联系起来的结构是可以独立变化的,如果使用继承会使类变的特别多)

学习地址

  • “抽象部分”和“实现部分”可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。
  • 一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展(比如咖啡的大杯中杯、小杯,加糖、加奶、加椰果等两个维度)。
  • 对于那些不希望使用继承或因为多层继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。


桥梁模式所涉及的角色有:

  • 抽象化(Abstraction)角色:抽象化给出的定义,并保存一个对实现化对象的引用
  • 修正抽象化(RefinedAbstraction)角色:扩展抽象化角色,改变和修正父类对抽象化的定义。
  • 实现化(Implementor)角色:这个角色给出实现化角色的接口,但不给出具体的实现。必须指出的是,这个接口不一定和抽象化角色的接口定义相同,实际上,这两个接口可以非常不一样。实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层的操作。
  • 具体实现化(ConcreteImplementor)角色:这个角色给出实现化角色接口的具体实现。

例子

第一步:创建抽象化部分:

//抽象化Abstraction
public abstract class Coffee 
    protected ICoffeeAdditives additives;
    public Coffee(ICoffeeAdditives additives)
        this.additives=additives;
    
    public abstract void orderCoffee(int count);

我们可以看到,Coffee持有了ICoffeeAdditives 引用,ICoffeeAdditives 的实例是通过构造函数注入的,这个过程就是我们所说的桥接过程。我们通过这个引用就可以调用ICoffeeAdditives的方法,进而将Coffee的行为与ICoffeeAdditives的行为通过orderCoffee()方法而组合起来。

下面是一个对抽象化修正的一个类,里面增加了一个品控的方法

//RefinedAbstraction
public abstract class RefinedCoffee extends Coffee 
    public RefinedCoffee(ICoffeeAdditives additives) 
        super(additives);
    
    public void checkQuality()
        Random ran=new Random();
        System.out.println(String.format("%s 添加%s",additives.getClass().getSimpleName(),ran.nextBoolean()?"太多":"正常"));
    

第二步:创建实现化部分

public interface ICoffeeAdditives 
    void addSomething();

//加奶
public class Milk implements ICoffeeAdditives 
    @Override
    public void addSomething() 
        System.out.println("加奶");
    

//加糖
public class Sugar implements ICoffeeAdditives 
    @Override
    public void addSomething() 
        System.out.println("加糖");
    

第三步:客户端调用

public static void main(String[] args) 
    //点两杯加奶的大杯咖啡
    RefinedCoffee largeWithMilk=new LargeCoffee(new Milk());
    largeWithMilk.orderCoffee(2);
    largeWithMilk.checkQuality();

输出结果:

加奶
大杯咖啡2杯
Milk 添加太多

三、组合模式(部分整体模式。整体与部分的结构,组合模式最主要的功能就是让用户可以一致对待整体和部分结构,将两者都作为一个相同的组件)

事实上组合模式和桥接模式的组合完全不一样。组合模式用于整体与部分的结构,当整体与部分有相似的结构,在操作时可以被一致对待时,就可以使用组合模式。例如:

  • 文件夹和子文件夹的关系:文件夹中可以存放文件,也可以新建文件夹,子文件夹也一样。
  • 总公司子公司的关系:总公司可以设立部门,也可以设立分公司,子公司也一样。
  • 树枝和分树枝的关系:树枝可以长出叶子,也可以长出树枝,分树枝也一样。

在这些关系中,虽然整体包含了部分,但无论整体或部分,都具有一致的行为。

  • 组合模式:又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。

组合模式最主要的功能就是让用户可以一致对待整体和部分结构,将两者都作为一个相同的组件,所以我们先新建一个抽象的组件类

例子

组合模式最主要的功能就是让用户可以一致对待整体和部分结构,将两者都作为一个相同的组件,所以我们先新建一个抽象的组件类:

public abstract class Component 
    // 职位
    private String position;
    // 工作内容
    private String job;

    public Component(String position, String job) 
        this.position = position;
        this.job = job;
    

    // 做自己的本职工作
    public void work() 
        System.out.println("我是" + position + ",我正在" + job);
    

    abstract void addComponent(Component component);

    abstract void removeComponent(Component component);

    abstract void check();

管理者继承自此抽象类:

Java

public class Manager extends Component 
    // 管理的组件
    private List<Component> components = new ArrayList<>();

    public Manager(String position, String job) 
        super(position, job);
    

    @Override
    public void addComponent(Component component) 
        components.add(component);
    

    @Override
    void removeComponent(Component component) 
        components.remove(component);
    

    // 检查下属
    @Override
    public void check() 
        work();
        for (Component component : components) 
            component.check();
        
    

职员同样继承自此抽象类:

public class Employee extends Component 

    public Employee(String position, String job) 
        super(position, job);
    

    @Override
    void addComponent(Component component) 
        System.out.println("职员没有管理权限");
    

    @Override
    void removeComponent(Component component) 
        System.out.println("职员没有管理权限");
    

    @Override
    void check() 
        work();
    

修改客户端如下:

public class Client 

    @Test
    public void test()
        Component boss = new Manager("老板", "唱怒放的生命");
        Component HR = new Employee("人力资源", "聊微信");
        Component PM = new Manager("产品经理", "不知道干啥");
        Component CFO = new Manager("财务主管", "看剧");
        Component CTO = new Manager("技术主管", "划水");
        Component UI = new Employee("设计师", "画画");
        Component operator = new Employee("运营人员", "兼职客服");
        Component webProgrammer = new Employee("程序员", "学习设计模式");
        Component backgroundProgrammer = new Employee("后台程序员", "CRUD");
        Component accountant = new Employee("会计", "背九九乘法表");
        Component clerk = new Employee("文员", "给老板递麦克风");
        boss.addComponent(HR);
        boss.addComponent(PM);
        boss.addComponent(CFO);
        PM.addComponent(UI);
        PM.addComponent(CTO);
        PM.addComponent(operator);
        CTO.addComponent(webProgrammer);
        CTO.addComponent(backgroundProgrammer);
        CFO.addComponent(accountant);
        CFO.addComponent(clerk);

        boss.check();
    

安全方式和透明方式

读者可能已经注意到了,Employee 类虽然继承了父类的 addComponent 和 removeComponent 方法,但是仅仅提供了一个空实现,因为 Employee 类是不支持添加和移除组件的。这样是否违背了接口隔离原则呢?

  • 接口隔离原则:客户端不应依赖它不需要的接口。如果一个接口在实现时,部分方法由于冗余被客户端空实现,则应该将接口拆分,让实现类只需依赖自己需要的接口方法

答案是肯定的,这样确实违背了接口隔离原则。这种方式在组合模式中被称作透明方式.

  • 透明方式:在 Component 中声明所有管理子对象的方法,包括 add 、remove 等,这样继承自 Component 的子类都具备了 add、remove 方法。对于外界来说叶节点和枝节点是透明的,它们具备完全一致的接口。

安全模式例子

将抽象类修改为:

public abstract class Component 
    // 职位
    private String position;
    // 工作内容
    private String job;

    public Component(String position, String job) 
        this.position = position;
        this.job = job;
    

    // 做自己的本职工作
    public void work() 
        System.out.println("我是" + position + ",我正在" + job);
    

    abstract void check();

可以看到,我们在父类中去掉了 addComponent 和 removeComponent 这两个抽象方法。

Manager 类修改为:

public class Manager extends Component 
    // 管理的组件
    private List<Component> components = new ArrayList<>();

    public Manager(String position, String job) 
        super(position, job);
    

    public void addComponent(Component component) 
        components.add(component);
    

    void removeComponent(Component component) 
        components.remove(component);
    

    // 检查下属
    @Override
    public void check() 
        work();
        for (Component component : components) 
            component.check();
        
    

Manager 类单独实现了 addComponent 和 removeComponent 这两个方法,去掉了 @Override 注解。

Employee 类修改为:

public class Employee extends Component 

    public Employee(String position, String job) 
        super(position, job);
    

    @Override
    void check() 
        work();
    

客户端建立人员结构关系:

public class Client 

    @Test
    public void test()
        Manager boss = new Manager("老板", "唱怒放的生命");
        Employee HR = new Employee("人力资源", "聊微信");
        Manager PM = new Manager("产品经理", "不知道干啥");
        Manager CFO = new Manager("财务主管", "看剧");
        Manager CTO = new Manager("技术主管", "划水");
        Employee UI = new Employee("设计师", "画画");
        Employee operator = new Employee("运营人员", "兼职客服");
        Employee webProgrammer = new Employee("程序员", "学习设计模式");
        Employee backgroundProgrammer = new Employee("后台程序员", "CRUD");
        Employee accountant = new Employee("会计", "背九九乘法表");
        Employee clerk = new Employee("文员", "给老板递麦克风");
        boss.addComponent(HR);
        boss.addComponent(PM);
        boss.addComponent(CFO);
        PM.addComponent(UI);
        PM.addComponent(CTO);
        PM.addComponent(operator);
        CTO.addComponent(webProgrammer);
        CTO.addComponent(backgroundProgrammer);
        CFO.addComponent(accountant);
        CFO.addComponent(clerk);

        boss.check();
    

组合模式中的安全方式与透明方式有什么区别?

  • 答案:透明方式:在 Component 中声明所有管理子对象的方法,包括 add 、remove 等,这样继承自 Component 的子类都具备了 add、remove 方法。对于外界来说叶节点和枝节点是透明的,它们具备完全一致的接口。

四、装饰者模式

装饰模式的主要作用就是:

  • 增强一个类原有的功能
  • 为一个类添加新的功能

装饰模式:动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰模式比生成子类实现更为灵活。其别名也可以称为包装器,与适配器模式的别名相同,但它们适用于不同的场合。根据翻译的不同,装饰模式也有人称之为“油漆工模式”。

用于增强功能的装饰模式

我们用程序来模拟一下戴上装饰品提高我们颜值的过程:

新建颜值接口:

public interface IBeauty 
    int getBeautyValue();

新建 Me 类,实现颜值接口:

public class Me implements IBeauty 

    @Override
    public int getBeautyValue() 
        return 100;
    

戒指装饰类,将 Me 包装起来:

public class RingDecorator implements IBeauty 
    private final IBeauty me;

    public RingDecorator(IBeauty me) 
        this.me = me;
    

    @Override
    public int getBeautyValue() 
        return me.getBeautyValue() + 20;
    

客户端测试:

Java

public class Client 
    @Test
    public void show() 
        IBeauty me = new Me();
        System.out.println("我原本的颜值:" + me.getBeautyValue());

        IBeauty meWithRing = new RingDecorator(me);
        System.out.println("戴上了戒指后,我的颜值:" + meWithRing.getBeautyValue());
    

运行程序,输出如下:

我原本的颜值:100
戴上了戒指后,我的颜值:120
这就是最简单的增强功能的装饰模式。以后我们可以添加更多的装饰类,比如:

耳环装饰类:

public class EarringDecorator implements IBeauty 
    private final IBeauty me;

    public EarringDecorator(IBeauty me) 
        this.me = me;
    

    @Override
    public int getBeautyValue() 
        return me.getBeautyValue() + 50;
    

项链装饰类:

public class NecklaceDecorator implements IBeauty 
    private final IBeauty me;

    public NecklaceDecorator(IBeauty me) 
        this.me = me;
    

    @Override
    public int getBeautyValue() 
        return me.getBeautyValue() + 80;
    

客户端测试:

public class Client 
    @Test
    public void show() 
        IBeauty me = new Me();
        System.out.println("我原本的颜值:" + me.getBeautyValue())设计模式--组合模式

Java 设计模式之组合学习与掌握

Java 设计模式之组合学习与掌握

组合模式(18)

设计模式--组合模式C++实现

入门设计模式之合成