• 【笔记】大话设计模式-567


    【笔记】大话设计模式-567

    5 依赖倒转原则

    5.1 定义

    A 高层模块不应该依赖低层模块。两个都应该依赖抽象类。

    B 抽象不应该依赖细节。细节应该依赖抽象。

    说白了,就是要针对接口编程,而不是要对实现编程。要设计抽象类,具体实现在子类中完成。

    举例来说:阿三的个人电脑PC里,如果CPU、内存、硬盘等都需要依赖具体的主板,主板一坏,他所有的部件就都没有用了。应该去耦合化,内存坏了不影响其他的功能部件。

    高层模块依赖低层模块:

    面向对象开发时,为了使得常用代码可以复用,一般都会将这些常用代码写成许多程序库,做新项目时,调用低层的函数就行了。就像如今ARM指令集这么火一样,就是将常用的80%指令集做了精简,在移动端调用时就非常方便、高效、低功耗,而剩余20%指令集,通过常用指令集拼接即可。

    所以根据原则A,这就是依赖倒转了。

    5.2 里氏代换原则

    里氏代换: 子类型必须能够替换掉它们的父类型。

    这就是C++中的多态动态绑定时子类可以初始化父类,在使用父类类型的模块时,在无需修改的情况下,代码可以扩展。可以提升代码的可用性,使代码更灵活,下降耦合性。

    子类拥有父类所有非private的行为和属性

    面向对象时,鸟和企鹅就不能设置为父类子类,因为鸟都能飞,而企鹅不能,所以企鹅不能继承鸟类。

    5.3 总结

    综合上面两个原则:

    只有当子类可以替换掉父类,软件单位的功能不能受到影响时,父类才能真正被复用,而子类也能够在父类的基础上增加新的行为。

    依赖倒转原则其实可以说是面向对象设计的标志,如果编写时考虑的都是如何针对抽象编程而不是针对细节的编程,即程序中所有的依赖关系都终止于抽象类或者接口,那就是面向对象的设计,反之,就是过程化的设计了。

    6 装饰模式

    6.1 定义

    动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更灵活。

    举个例子:比如阿三今天要去逛街,那么他要穿什么衣服呢?可以通过不同的装饰器选择不同的服装搭配。这就是装饰模式。

    6.2 具体实现代码

    #region 6 装饰模式
    // 父类: Component类
    // 父类为抽象类,方便扩展
    abstract class Component
    {
        public abstract void Operation();
    }
    
    // 实体类: ConcreteComponent类,继承于Component
    class ConcreteComponent : Component
    {
        public override void Operation()
        {
            Console.WriteLine("具体对象的操作");
        }
    }
    
    // 装饰类: Decorator类,同样继承于Component
    // 该类为抽象类,以便后面添加具体功能
    abstract class Decorator: Component
    {
        // 父类为成员对象,接受实体类对象
        protected Component component;
    
        public void SetComponent(Component component)
        {
            this.component = component;
        }
    
        public override void Operation()
        {
            if (component != null)
            {
                component.Operation();
            }
        }
    }
    
    // ConcreteDecoratorA 类,继承Decorator类
    class ConcreteDecoratorA : Decorator
    {
        // 该类独有的功能
        private string addedState;
    
        public override void Operation()
        {
            // 运行原Component 的Operation()
            base.Operation();
            addedState = "New State";
            Console.WriteLine("具体装饰对象A的操作");
        }
    }
    
    // ConcreteDecoratorB 类,继承Decorator类
    class ConcreteDecoratorB : Decorator
    {
        // 该类独有的功能
        private void AddedBehavior()
        {
    
        }
    
        public override void Operation()
        {
            // 运行原Component 的Operation()
            base.Operation();
            AddedBehavior();
            Console.WriteLine("具体装饰对象B的操作");
        }
    }
    // 客户端代码
    static void Main(string[] args)
    {
        // 用 ConcreteComponent 实例化对象 c
        ConcreteComponent c = new ConcreteComponent();
        // 用装饰类A 实例化对象d1
        ConcreteDecoratorA d1 = new ConcreteDecoratorA();
        // 用装饰类B 实例化对象d2
        ConcreteDecoratorB d2 = new ConcreteDecoratorB();
    
       // 用对象d1 包装 c
        d1.SetComponent(c);
        // 用对象d2 包装 d1
        d2.SetComponent(d1);
        // 执行d2的 Operation()
        d2.Operation();
    
        Console.Read();
    }
    
    #endregion
    
    • 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
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    结果如下:

    就是利用SetComponent对对象进行包装,每个装饰对象的实现就和如何使用这个对象分开了,每个装饰对象只关心自己的功能,不需要关心被添加到对象链当中。

    精髓就是实例化对象和装饰对象都继承于同一个父对象,这样子类都可以用父对象表示,就可以对实例化的父对象进行操作了,而且操作过程并不在对象的子类中。

    变换思维:

    如果没有共同的父对象Component,那么装饰类就可以作为Decorator的子类了,同理,装饰类Decorator和其子类ConcreteDecorator也可以合并成一个类

    6.3 总结

    什么时候使用装饰模式?

    当系统需要更新功能的时候,是向旧代码中添加新的代码。这些新加的代码通常装饰了原有类的核心职责或主要行为。

    如果加入到主类中,会增加主类的复杂度。

    如果仅仅满足一些只在特定情况下才会执行的特殊行为需要,就可以用装饰模式,把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象。

    当执行特殊行为时,客户代码就可以在运行时根据需要有选择地、按顺序地使用装饰功能包装对象了。

    优点

    • 把类中的装饰功能从类中搬移去除,简化原有的类
    • 有效地把类的核心职责和装饰功能区分开了,有效去除了相关类中重复的装饰逻辑

    7 代理模式

    7.1 定义

    Proxy: 为其他对象提供一种代理,以控制对这个对象的访问

    什么场景下需要使用代理模式呢?

    **举个例子:**比如阿三想追求一个漂亮妹子,但是他又不好意思,想让大头帮忙送一些礼物或者信件,这时候,大头就是阿三的代理,大头的行为是代替阿三做出来的,实际表达人是阿三,但是与妹子的接触确实大头。

    7.2 结构

    与装饰模式有点像。首先定义一个共同的接口类Subject,该类中定义一些方法,然后再实现两个类:真实实体类RealSubject和代理类Proxy,真实实体类具体实现接口,代理类中申明一个真实实体对象,并同样生成相关方法,只不过实现方式是通过调用成员变量——真实实体实例化对象的方法,并不是自己的方法。

    #region 代理模式
    
    // 定义主接口,使得使用RealSubject的地方都能使用Proxy
    abstract class Subject
    {
        public abstract void Request();
    }
    
    // 定义Proxy所代表的真实实体
    class RealSubject : Subject
    {
        public override void Request()
        {
            Console.WriteLine("真实的请求");
        }
    }
    
    // 代理类,保存一个引用,使得代理可以访问实体,并提供一个与Subject的接口相同的接口,这样代理就可以用来替代实体
    class Proxy : Subject
    {
        RealSubject realSubject;
        public override void Request()
        {
            if (realSubject == null)
            {
                realSubject = new RealSubject();
            }
            realSubject.Request();
        }
    }
    
    // 客户端代码
    static void Main(string[] args)
    {
        Proxy proxy = new Proxy();
        proxy.Request();
    
        Console.Read();
    }
    
    • 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

    运行结果:

    7.3 应用场合

    7.3.1 远程代理

    为一个对象在不同的地址空间提供局部代表,这样可以隐藏一个对象存在于不同地址空间的事实。

    **远程访问就是使用的代理模式:**比如WebService.Net的应用时,在应用程序的项目中引入一个Web引用,引用一个WebService,此时会在项目中生成一个WebReference的文件夹和一些文件,其实它们就是代理,使得客户端程序调用代理就可以解决远程访问问题。

    7.3.2 虚拟代理

    根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。

    浏览器用代理模式优化下载:比如阿三打开一个很大的HTML网页时,里面可能有很多文字和图片,但是你可以很快打开它,此时你能看到的时所有的文字,图片却是一张一张下载后才能看到的,那些未打开的图片框,就是通过虚拟代理来代替了真实的图片,此时代理存储了真实图片的路径和尺寸。

    7.3.3 安全代理

    控制真实对象访问时的权限,一般用于对象应该有不同的访问权限的时候。

    **举个例子:**比如阿三和大头都想访问吃播的视频,这时候,代理根据输入的对象时阿三和大头,反馈不同的结果。

    7.3.4 智能指引

    当调用真实的对象时,代理处理另外一些事。

    • 如计算真实对象的引用次数,这样当没有对象引用时,可以自动释放;
    • 当第一次引用一个持久对象时,将它装入内存;
    • 在访问一个实际对象时,检查是否已经锁定它,确保其他对象不能改变它。

    代理模式就是访问对象时引入的一定程度的间接性,有了这种间接性,可以附加多种用途。说白了,就是代理中有一个对象,在传入该对象时,我们可以直接反馈对象的方法,也可以对对象做一些额外的操作。

  • 相关阅读:
    【单片机毕业设计】【mcuclub-jj-003】基于单片机的八层电梯的设计
    for语句
    Hdfs梳理
    《向量数据库指南》——宏观解读向量数据库Milvus Cloud
    服务治理-Eureka
    阻塞队列LinkedBlockingQueue 源码解析
    Spring简介
    Day3-渐渐明白[链表]的边界
    Echarts 折线图的详细配置过程
    基于袋獾算法的无人机航迹规划-附代码
  • 原文地址:https://blog.csdn.net/moneymyone/article/details/126002106