设计模式和原则:原则篇(二)

设计模式和原则索引



二、基于接口,而不是实现编程

让类型间的依赖基于接口,来最小化类型间的依赖。在代码变化的可能性较大时,这种设计方法非常适合。

>应用接口还是抽象类?要根据具体情况判断,如果你希望接口附带一些行为,就是用抽象类,否则使用接口,DotNet不支持多重继承,使用interface则基类还有选择的余地。

接口可以是.Net中的Interface也可以是一个基类。

基于接口的核心在于多态,可以延迟实现,也可以运行时动态设定


三、(尽量)使用对象组合,而不是类型继承
GOF中提出了两种重用的方式:白盒重用和黑盒重用。
白盒重用既是继承,在类型继承中,派生类不仅继承了父类的元素和功能,还继承了父类型的上下文,对派生类的重写可能会破坏父类的上下文,而且父类的修改也会破坏到派生类,这是继承带来的问题。

黑盒重用就是对象组合,通过创建一个新的类型,包含基类的实例来实现,如下:

namespace fofo.DesignPattern
{
    public class MyClassExtension
    {
        private MyClass myObj = null;

        public MyClassExtension()
        {
            myObj = new MyClass();
        }

        public object DoWork()
        {
            object data = myObj.DoWork();

            // process data
            return data;
        }

        // oth code
    }
}

这样MyClassExtension就以黑盒的方式通过定义的契约使用了MyClass,而且MyClassExtension无法访问MyClass的内部成员,也不能修改其行为,MyClassExtension将具体的功能交给MyClass实现,并进行功能上的增强。
那么什么时候使用继承呢,一般来说,派生类需要为原类型添加一个新的功能,或者完全重写一个基类的某个功能时才使用继承,
而且组合无法实现多态,系统需要多态的情况下,使用继承。

四、开放/封闭原则(OCP)
系统应该对扩展开放,对修改关闭。
满足这个原则的系统,可以更灵活的应对需求变化。在系统设计中,OCP建议在发生变化时,不要修改现有系统的代码,而是通过增加新的代码来增强现有类型的功能(这意味着使用继承或组合)

五、派生类可以代替父类使用
在系统设计时,某个派生类应该可以适用于任何可以使用其父类的地方。
这条和前面的OCP紧密相关,违反此条的也必定违反了OCP。如下代码:

namespace fofo.DesignPattern
{
    public class MyClass
    {
        private int nStatus = 0;

        public virtual object DoWork()
        {
            object objRet = null;
            if (nStatus == 0)
            {
                 //...
            }
            else
            {
                 //...
            }

            return objRet;
        }
    }
}

这个类的派生类无法访问父类的private成员,不能代替父类使用。
在设计虚方法时不应该访问私有成员,因为私有成员的访问无法在派生方法中实现。一般来说,派生类重写的虚方法应该和父类完成同样的功能,二者的前置条件和后置条件都应该相同。
不满足此原则必定会破坏多态性。

六、控制反转(依赖倒置原则 DIP)
面向对象中,系统是对现实的抽象表示,这个过程应该是自上而下的,先对系统进行高层次的抽象,逐步产生一系列的模块、类型和方法,
如下代码,一个View可简单拆分为DataLoader(获取数据)和Renderer(数据展现):

namespace fofo.DesignPattern
{
    public class MyView
    {
        public void Show()
        {
            IDataLoader loader = ResolveDataLoader();
            DataContext data = loader.GetViewData();

            IRenderer renderer = ResolveRenderer();
            renderer.RenderView(data);
        }
        //... other code
    }
}

View的Show方法并不依赖于DataLoader和Rederer的实现,他们在固定的接口下隐藏了实现的细节。
也即依赖导致原则:高层次的组件(如View)不应该依赖于低层次的组件(如DataLoader和Renderer),二者应依赖于接口,抽象不依赖于细节,细节依赖于抽象。
名字里的“倒置”表示在设计过程中采用自上而下的方式,且更应该关注高层次的工作流,而不是低层次的具体实现。
完善下上面的例子,用注入实现:

namespace fofo.DesignPattern
{
    public class MyView
    {
        private IDataLoader _loader = null;
        private IRenderer _renderer = null;

        public MyView(IDataLoader loader, IRenderer renderer)
        {
            _loader = loader;
            _renderer = renderer;
        }

        public void Show()
        {            
            DataContext data = loader.GetViewData();
         
            renderer.RenderView(data);
        }

        //... other code
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值