23种设计模式归纳总结——结构型

Posted 化作孤岛的瓜

tags:

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

目录

5.代理模式

6.桥接模式

7.装饰器模式

8.适配器模式

9.门面模式

10.组合模式

11.享元模式


结构型设计模式主要解决“类或对象的组合或组装”问题

5.代理模式

在不直接修改原有类的前提下对原有类的方法功能进行扩展。

通过引入代理类(构参带入)来给原始类附加功能

示例:

定义一个绘制api DrawApi, 需要在绘制前后进行一些扩展功能(打点,监控等)

1.静态代理

缺点:
1:如果原始类有多个方法,我们需要在代理类中,将原始类中的所有的方法,都重新实现一遍
2:如果存在多个被代理类(比如下面的绘制 和 测算两个类) 都需要通过代理模式添加特殊功能,
则需要针对每个被代理类都创建一个代理类

//定义一个绘制接口(原始类)
    interface DrawApi 
        void draw();

        void refresh();
    

    //定义具体的圆形绘制方案,被代理类
    static class CircleDraw implements DrawApi 

        @Override
        public void draw() 
            System.out.println("画一个圆");
        

        @Override
        public void refresh() 
            System.out.println("刷新");
        
    

    //定义一个测算接口,原始类
    interface CalculateApi 
        void calculate();
    

    //定义一个绘制代理类,代理实现具体的绘制功能,代理类
    static class DrawProxy implements DrawApi 
        //被代理的绘制方案
        private DrawApi target;

        //构造器
        public DrawProxy() 
            this.target = new CircleDraw();
        

        @Override
        public void draw() 
            before();
            target.draw();
            after();
        

        @Override
        public void refresh() 
            //缺点:我们需要在代理类中,将原始类中的所有的方法,都重新实现一遍
        

        private void after() 
            System.out.println("执行后 做一些事情");
        

        private void before() 
            System.out.println("执行前 做一些事情");
        

    

    public static void main(String[] args) 
        //使用静态代理
        new DrawProxy().draw();
        // 如果需要扩展其他原始类,则这样行不通
        //new DrawProxy(new CalculateApi()).draw();
        //线程也用到了代理模式,传入不同的runnable实现
        //new Thread(实现runnable接口的实例化对象).start();
    

2.动态代理

主要是为了解决静态代理的两个缺点

//定义一个绘制接口,原始类
    interface DrawApi 
        void draw();

        void refresh();
    

    //定义具体的圆形绘制方案,被代理类
    static class CircleDraw implements DrawApi 

        @Override
        public void draw() 
            System.out.println("画一个圆");
        

        @Override
        public void refresh() 
            System.out.println("刷新");
        
    

    //定义一个测算接口,原始类
    interface CalculateApi 
        void calculate();
    

    //定义圆形的测算方案,被代理类
    static class CircleCalculate implements CalculateApi 

        @Override
        public void calculate() 
            System.out.println("测量圆的尺寸");
        
    

    //动态代理实现类
    //优点:不需要将将原始类中的所有的方法,都重新实现一遍
    public static class DrawProxy 
        public DrawProxy() 
        

        public Object createProxy(Object proxiedObject) 
            Class<?>[] interfaces = proxiedObject.getClass().getInterfaces();
            DynamicProxyHandler handler = new DynamicProxyHandler(proxiedObject);
            return Proxy.newProxyInstance(proxiedObject.getClass()
                                                       .getClassLoader(), interfaces, handler);
        

        //代理类的实现处理接口
        private class DynamicProxyHandler implements InvocationHandler 
            private final Object drawImpl;

            public DynamicProxyHandler(Object drawImpl) 
                this.drawImpl = drawImpl;
            

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
                before();
                Object result = method.invoke(drawImpl, args);
                String apiName = drawImpl.getClass().getName() + ":" + method.getName();
                System.out.println("执行方法:" + apiName);
                after();
                return result;
            

            private void after() 
                System.out.println("执行后 做一些事情");
            

            private void before() 
                System.out.println("执行前 做一些事情");
            
        
    

    public static void main(String[] args) 
        DrawProxy proxy = new DrawProxy();
        DrawApi drawApi = (DrawApi) proxy.createProxy(new CircleDraw());
        drawApi.draw();

        //完美解决 原始类有多个的问题
        CalculateApi calculateApi = (CalculateApi) proxy.createProxy(new CircleCalculate());
        calculateApi.calculate();
    

6.桥接模式

概念:
与代理模式容易混淆。一个类存在两个(或多个)独立变化的维度,我们通过组合的方式,让这两个(或多个)维度可以独立进行扩展。
通俗的说,就是一个类如果有多个属性需要变化,在进行扩展(继承)时会导致新增的子类指数增加,所以需要通过桥接模式将继承改为组合的方式,
将属性的维度解藕出来,将抽象与原本复杂的实现分离,提高扩展能力。


区别:
与代理模式相比,假设将一个纬度抽象以后,执行的方法命名为a()
代理模式侧重于 通过代理的方式扩展被代理类的功能
桥接模式侧重于 通过组合的方式让不同的纬度的原始类可以搭配使用

示例:
android画图举例,需要提供一个接口DrawApi,可以绘制三种颜色,与三种形状,普通的做法是通过子类实现不同的组合,一共有9种子类。
这里把颜色解藕然后通过构参带入,把形状解藕为通过子类实现,使其变为两个自由的纬度,通过组合关系(也就是桥梁)任意组合在一起,
可以大幅减少实现的类型。

//颜色抽象化,原始类
    interface Color 
        String getColor();
    

    //具体的红色,原始类实现
    static class RedColor implements Color 
        @Override
        public String getColor() 
            return "红色";
        
    

    //创建桥接的实现接口。画图api
    static abstract class DrawApi 
        protected Color mColor;

        public DrawApi(final Color color) 
            mColor = color;
        

        abstract void draw();
    

    //桥接模式实现类
    static class CircleDraw extends DrawApi 
        public CircleDraw(final Color color) 
            super(color);
        

        @Override
        void draw() 
            System.out.println("画一个 " + mColor.getColor() + " 的圆形");
        
    

    public static void main(String[] args) 
        DrawApi drawApi = new CircleDraw(new RedColor());
        drawApi.draw();
    

7.装饰器模式

概念:
装饰器模式是指给一个类增强一些方法,对其做一些包装,但是不会影响改变原本类。


异同:
1.与代理模式的区别
装饰器模式看起来跟代理模式特别的相似,但是
对于代理模式来说可以作出一些操作改变原有代码,也就是说带有侵入性。
装饰者模式侧重于添加添加额外的功能职责,也可以重叠使用。
2.与继承的区别:
继承设计子类,是在编译时静态决定的,通过组合的做法扩展对象,可以在运行时动态地进行扩展
装饰者模式通过组合和委托,可以在运行时动态地为对象加上新的行为

示例:
需要一个画图api,在draw之后,需要设置颜色和形状

//画图api抽象类
    static abstract class DrawComponent 
        abstract void draw();
    

    static class DrawApi extends DrawComponent 

        @Override
        void draw() 
            System.out.println("画图");
        
    

    /**
     * Decorator,装饰抽象类,继承了Component
     * 从外类来扩展Component类的功能,但对于Component来说,
     * 是无需知道Decorator的存在的
     */
    static abstract class DrawDecorator extends DrawComponent 
        protected DrawComponent component;

        public DrawComponent getComponent() 
            return component;
        

        public void setComponent(final DrawComponent component) 
            this.component = component;
        

        @Override
        void draw() 
            if (component != null) 
                component.draw();
            
        
    

    static class ShapeDrawDecorator extends DrawDecorator 
        @Override
        void draw() 
            super.draw();
            System.out.println("设置形状");
        
    

    static class ColorDrawDecorator extends DrawDecorator 
        @Override
        void draw() 
            super.draw();
            System.out.println("设置颜色");
        
    

    public static void main(String[] args) 
        DrawApi drawApi = new DrawApi();
        ShapeDrawDecorator shapeDrawDecorator = new ShapeDrawDecorator();
        shapeDrawDecorator.setComponent(drawApi);
        ColorDrawDecorator colorDrawDecorator = new ColorDrawDecorator();
        colorDrawDecorator.setComponent(shapeDrawDecorator);
        colorDrawDecorator.draw();
    

8.适配器模式

概念:
将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。(比如Android中列表的Adapter)
分为类适配器,对象适配器
IDrawApi为绘图接口,Printer为不兼容IDrawApi的打印器功能类,Adapter通过继承或者组合兼容两种功能


问题:
应该选择类适配器还是对象适配器?
如果Printer接口并不多,那两种实现方式都可以。
如果Printer接口很多,而且Printer和IDrawApi接口定义大部分都相同,那我们推荐使用类适配器,因为Adaptor复用父类Printer的接口,比起对象适配器的实现方式,Adaptor的代码量要少一些。
如果Printer接口很多,而且Printer和IDrawApi接口定义大部分都不相同,那我们推荐使用对象适配器,因为组合结构相对于继承更加灵活。

示例:
类适配器:基于继承实现

/**
     * 类适配器,基于继承
     */
    interface IDrawApi 
        void draw();
    

    /**
     * 打印
     */
    static class Printer 
        public void print() 
            System.out.println("打印");
        
    

    /**
     * Adapter既可以绘画也可以打印
     */
    static class Adapter extends Printer implements IDrawApi 

        @Override
        public void draw() 
            System.out.println("绘画");
        

        @Override
        public void print() 
            super.print();
        
    


    public static void main(String[] args) 
        Adapter adapter = new Adapter();
        adapter.draw();
        adapter.print();
    

示例:
对象适配器:基于组合实现

/**
     * 绘图
     */
    interface IDrawApi 
        void draw();
    

    /**
     * 打印
     */
    static class Printer 
        public void print() 
            System.out.println("打印");
        
    

    /**
     * Adapter既可以绘画也可以打印
     */
    static class Adapter implements IDrawApi 
        private Printer mPrinter;

        public Adapter(final Printer printer) 
            mPrinter = printer;
        

        @Override
        public void draw() 
            System.out.println("绘画");
        

        public void print() 
            mPrinter.print();
        
    


    public static void main(String[] args) 
        Adapter adapter = new Adapter(new Printer());
        adapter.draw();
        adapter.print();
    

9.门面模式

概念:
也叫外观模式,为子系统提供一组统一的接口,定义一组高层接口让子系统更易用。
简单来说,就是将多个接口调用替换为一个门面接口调用。

static class TextHelper 
        public void print() 
            System.out.println("写字");
        
    

    static class DrawHelper 
        public void print() 
            System.out.println("画图");
        
    

    static class PrinterManager 
        TextHelper mTextHelper;

        DrawHelper mDrawHelper;

        public PrinterManager(final TextHelper textHelper, final DrawHelper drawHelper) 
            mTextHelper = textHelper;
            mDrawHelper = drawHelper;
        

        public void print() 
            if (mTextHelper != null) 
                mTextHelper.print();
            
            if (mDrawHelper != null) 
                mDrawHelper.print();
            
        
    

    public static void main(String[] args) 
        PrinterManager printerManager = new PrinterManager(new TextHelper(), new DrawHelper());
        printerManager.print();
    

10.组合模式

概念:
将一组对象组织(Compose)成树形结构,以表示一种“部分-整体”的层次结构。
组合让客户端(在很多设计模式书籍中,“客户端”代指代码的使用者。)可以统一单个对象和组合对象的处理逻辑。


示例:
希望在内存中构建整个公司的人员架构图(部门、子部门、员工的隶属关系),
并且提供接口计算出部门的薪资成本(隶属于这个部门的所有员工的薪资和)。
采用组合的方式可以更方便的处理员工与部门的工资计算。

//人力资源
    static abstract class HumanResource 
        protected long id;
        protected double salary;

        public HumanResource(long id) 
            this.id = id;
        

        public long getId() 
            return id;
        

        public abstract double calculateSalary();
    

    //员工
    static class Employee extends HumanResource 
        public Employee(long id, double salary) 
            super(id);
            this.salary = salary;
        

        @Override
        public double calculateSalary() 
            return salary;
        
    

    //部门
    public static class Department extends HumanResource 
        private List<HumanResource> subNodes = new ArrayList<>();

        public Department(long id) 
            super(id);
        

        @Override
        public double calculateSalary() 
            double totalSalary = 0;
            for (HumanResource hr : subNodes) 
                totalSalary += hr.calculateSalary();
            
            this.salary = totalSalary;
            return totalSalary;
        

        public void addSubNode(HumanResource hr) 
            subNodes.add(hr);
        
    

    public static void main(String[] args) 
        //it部门
        Department itDepartment = new Department(1);
        itDepartment.addSubNode(new Employee(1, 5000));
        itDepartment.addSubNode(new Employee(2, 5100));
        itDepartment.addSubNode(new Employee(3, 5200));
        itDepartment.addSubNode(new Employee(4, 5300));
        itDepartment.addSubNode(new Employee(5, 5400));
        //客户部门
        Department customerDepartment = new Department(1);
        customerDepartment.addSubNode(new Employee(1, 1000));
        customerDepartment.addSubNode(new Employee(2, 1100));
        customerDepartment.addSubNode(new Employee(3, 1200));
        customerDepartment.addSubNode(new Employee(4, 1300));
        customerDepartment.addSubNode(new Employee(5, 1400));

        //总薪水
        double salaryAll = itDepartment.calculateSalary() + customerDepartment.calculateSalary();
        System.out.println("总薪水:" + salaryAll);
    

11.享元模式

概念:
当一个系统中存在大量重复对象的时候,如果这些重复的对象是不可变对象,
我们就可以利用享元模式将对象设计成享元,在内存中只保留一份实例,供多处代码引用


示例:
直播间需要实现弹幕表情功能,每一个弹幕的id和表情是固定的,但是x,y坐标不同。

// 享元类,弹幕
    static class BarrageUnit 
        public int id;
        private String face;

        public BarrageUnit(final int id, final String face) 
            this.id = id;
            this.face = face;
        

        @Override
        public String toString() 
            return "BarrageUnit" +
                   "id=" + id +
                   ", face='" + face + '\\'' +
                   '';
        
    

    //弹幕类
    static class Barrage 
        public BarrageUnit mBarrageUnit;
        public float x, y;

        public Barrage(final BarrageUnit barrageUnit, final float x, final float y) 
            mBarrageUnit = barrageUnit;
            this.x = x;
            this.y = y;
        

        @Override
        public String toString() 
            return "Barrage" +
                   "mBarrageUnit=" + mBarrageUnit +
                   ", x=" + x +
                   ", y=" + y +
                   '';
        
    


    static class ChessPieceUnitFactory 
        private static final Map<Integer, BarrageUnit> barrages = new HashMap<>();

        static 
            barrages.put(1, new BarrageUnit(1, "微笑"));
            barrages.put(2, new BarrageUnit(2, "愤怒"));
            barrages.put(3, new BarrageUnit(3, "狗头"));
            //...省略初始化其他弹幕的代码...
        

        public static BarrageUnit getChessPiece(int barrageId) 
            return barrages.get(barrageId);
        
    

    static class LiveRoom 
        private final Map<Integer, Barrage> barrages = new HashMap<>();

        //展示表情弹幕
        public void showFace(int barrageId, int toPositionX, int toPositionY) 
            barrages.put(barrageId, new Barrage(
              ChessPieceUnitFactory.getChessPiece(barrageId), toPositionX, toPositionX));
        

        public void draw() 
            System.out.println("绘制弹幕:" + barrages.toString());
        
    

    public static void main(String[] args) 
        LiveRoom liveRoom = new LiveRoom();
        liveRoom.showFace(1,100,100);
        liveRoom.showFace(2,200,200);
        liveRoom.showFace(3,300,300);
        liveRoom.draw();

    

以上是关于23种设计模式归纳总结——结构型的主要内容,如果未能解决你的问题,请参考以下文章

23种设计模式归纳总结——结构型

23种设计模式归纳总结——结构型

23种设计模式归纳总结——创建型

23种设计模式归纳总结——创建型

23种设计模式归纳总结——创建型

23种设计模式归纳总结——创建型