基类 的薄弱之处

                                         基类 的薄弱之处

       这个怎么解释呢?比如说:程序总是由人来设计与编写的,所以工作开始时考虑不到某些问题当然也是很正常的事,所以可能在工作进行了一段时间后发现基类需要变更。

       你想, 如果我在基类中更改了成员的数据类型,以及那些允许重写的那些方法和属性, 那派生类及其子类还能正常工作吗?尤其是当一个团队中的多个开发人员一起来创作基类和派生类时,就更是要命了。

       很多情况下,大家可能已经把基类和一些派生类编译成二进制形式进行提交了。更改基类,重新编译再分布,会牵一发而 动全身,导致项目的崩溃。

       所以我们把这叫做‘脆弱的基类’。也就是说像蛋壳一样不堪一击喽。它是整个工程中最薄弱最致命的环节。

       如果对项目的前期设计考虑尽可能周详,在工程实施中对项目的代码控制与相关性分析做得踏实,会起到很好的效果。但是不管一个人如何努力,有时还是无法避免对基类进行不可预见的更改。

       所谓成事在人,经过程序员们的摸索,有了一些处理的手段,不过并不是什么完美解决方案,只能在某种程度上减轻危害。

       我们常用的一个方法,最直接的思想就是,把有可能发生更改的,全都放在派生类中进行,不在基类中做。

       这具体是什么意思呢?

       我们在基类中使用的是抽象类,它内含的方法与属性只有定义,没有进行实现,而把实现部分都放在派生类中做。

       这样一来,抽象类自身是无法被实例化的。但是它的好处不言而喻, 就是有可能发生实现上的更改,都会只涉及到它的派生类了。VB.NET 中就提供了这样的手段。

来看一小段代码:

Module Module1
    'MustInherit 关键字此类只能作为基类使用,并且不能直接在里面创建实例.
    Public MustInherit Class BaseClass
        'MustOverride 关键字表示此属性或过程未在类中实现,且在实现之前须在派生类中重写可
        Public MustOverride Sub OutputA(ByVal x As Long)
        Public MustOverride Function GetValue(ByVal x As Integer) As Long

    End Class

    Public Class DerivedClass
        Inherits BaseClass
        Dim strA As Char
        Public Overrides Function GetValue(x As Integer) As Long
            Dim i As Long = x * Asc(strA)
            Return i
        End Function

        Public Overrides Sub OutputA(ByVal x As Long)
            strA = Chr(x + 10)
        End Sub
    End Class


    Sub Main()
        Dim MyClassA As New DerivedClass
        MyClassA.OutputA(10) '实例化派生类后,使用重写基类的方法 InputA
        Console.WriteLine("访问派生中重写的基类函数 GetValue 后得到的值为: {0}", MyClassA.GetValue(100))
        Console.Read()
    End Sub

End Module

 

      这里要注意两个问题:

       一个是关键字,我们用 MustInherit 来修饰类名,使类成为抽象类,在它的成员中,把方法和属性前加入 MustOverride 修饰符表示它们必须在派生类中加以实现。

      第二个要注意的是,派生类必须对所有用 MustOverride 标识的基类方法和属性都进行实现,只重写了 OutputA,不写 GetValue 编译器会报错的。

      大家可能已经发现了,这的确可以解决一部分问题,但好象只能解决在基类中进行实现的代码有更改的问题,对于数据类型的的更改好象没有什么效果!!

      所以我刚才说,是在某种程度上,但解决基类中数据类型的更改,并不麻烦,我们可以在派生类中用 Shadows 来解决呀! Shadows 关键字的作用与使用方法,我在上篇的 “类 的重载(Overloads)与隐藏(Shadows)” 已介绍过了,不懂的可以先去看看 ,这里略回顾一下对它的总结:

       如果基类设计有误而又无法得到源码,或者基类适用大多情况,但有特殊情况时又得改写。

       由于基类中方法设计时就是不允许重写(没有 Overridable ),这里想在子类中“改写”这个方法,怎么办?当然是用 Shadows  隐藏基类的同名方法。

       简单地说:重写是征得(Overridable)允许后才可改写;而隐藏则是未经允许就强行改写基类的方法。

       当声明一个方法,如果不是用 Overrides 关键字,它就是非虚拟方法,而非虚拟方法是不能被子类重写和替代的方法, 而隐藏它要重写非虚拟方法,不管它们是否声明时使用了 Overridable ,它无视规则。

       所以,隐藏比重写更霸道,如果说重写是有法有依的司法人员,那么隐藏就是无法无天的抢劫犯。

       但基类如果要做功能扩展,怎么办呢?其实,最安全的方法是添加新成员,而不是对基类的大肆修改。

       一般是往派生类添加设计时缺失的新成员。最好不要使用 Overloads 关键字来命名与基类相同的成员,那样往往会带给你意想不到的问题。

       最好重新定义新成员,命名上也要尽量与基类已有的成员名区分开来。其实,也可以往抽象类基类中添加新成员的定义,但这样一来,需要为基类制定版本,虽然不会对应用程序造成毁灭性的危害,但是应该要能够完全地控制与管理自己的代码。

        我们一般是不希望扩展基类的。 意思是指基类的脆弱问题实际上是客观存在的,我们所做的就是要最大程度的减小这个问题带来的危害?

        没错,对于一个应用程序的设计者来讲,想使用面向对象方法来开发,必须要在设计的时候精心 策划类的层次结构。

        一般来说,是有这样几个准则需要把握的:

        第一,遵循先通用,再专用的原则。 先设计好层次结构中每一级别的类中的通用部分,也就是提供给派生类继承的成员和标识为 Public 的成员;

        第二,在定义数据类型和存储区域时要有预留量,以避免以后更改困难。 例如,即使当前数据可能仅需要 Integer 类型就够了,在设计时我们使用 Long 类型的变量。当然,最好能物尽其用,也不要盲目放大;

        第三,在一个项目中,必须统一管理与分配团队中使用的所有的命名,以减少命名冲突。这一点其实事关重大;

        第四, 要使用提供可行的最低访问权限的访问修饰符声明类成员。

       内部类成员应声明为 Private;仅在类内与派生类中才需要的成员应标记为 Protected;外部访问类的数据成员可以标记为 Friend ,但仅限于该模块是定义该类的项目的一个组成部分;使用 Public 标识的成员,只能是实例化时真正需要的内容,而且经常用在类层次结构的底部,也就是说,一个规范的操作,标准的命名体系可以决定基类的强壮与否。

       不难想象,后续的种种措施,就是决定是给脆弱的基类穿上多厚的防护衣,因为基类始终都是薄弱的 。

       我们要在工程实践中不断地学习与磨练,了解更多的知识,获得 更多的经验,这样才会成长为一名合格的程序设计师。

       就拿继承来说吧,在.NET 中其实支持三种继承方式:实现继承、接口继承、可视继承。我们其实只用了第一种继承方式!

 

转载于:https://www.cnblogs.com/lfls128/p/4971871.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值