【笔记】大话设计模式-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

结果如下:

就是利用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();
}

运行结果:

7.3 应用场合

7.3.1 远程代理

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

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

7.3.2 虚拟代理

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

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

7.3.3 安全代理

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

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

7.3.4 智能指引

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值