Flicker1985's Blog

Everything should be made as simple as possible, but not simpler.
浅谈封装


  What 


     “封装是指对外隐藏对象的属性和内部实现的细节,只提供对外的接口访问”。


        Why


  我们为什么要封装对象内部的属性和实现细节呢?举个例子,现在我们有一个对象A和另一个对象B,如果我们将A的属性或者内部的实现细节直接暴露给B,那么B对A的属性和内部的实现细节就产生了依赖。这样的话,一旦A的属性或者内部的一些实现细节发生变化,必然影响B。这样的结果是我们所不愿意看到的,所以我需要封装A的属性和内部实现细节,也就是说我们要封装A属性和内部实现细节的变化,进一步说我们就是要封装变化。得到这样的结论,我觉得我们对封装的定义也可以有进一步的延伸-封装就是要隐藏变化。

  面向对象软件设计之中我们遇到的变化又何止一个对象的属性和内部实现细节这么简单呢?比如我们现在有这样一个需求,我们的程序在启动的时候需要读一个数据文件,数据的内容是机密客户不愿意告诉我们,但是可以告诉我们数据的格式,且现有的数据文件时txt格式的。

public class Configuration

{

  public void reader()

  {....}

}

我们很快实现了这个需求,而且测试也通过了。正在我们得意于自己高超的编程技巧的时候,客户发话了“不好意思,昨天我们突然发现我们还有一些数据文件的格式时Xml的格式。这时候我们怎么办?告诉客户让他们把那些xml格式的数据自己改成txt格式吗?如果你有这样的口才,那当然最好。如果没有的话,我想你也只有自己去改程序了。怎么改呢?

public class Configuration

{

  public void reader()

    {...}

  public void readerXml()

  {....}

}

 

客户的新需求我们实现了,我们又一次证明了自己高超的编程技巧。但是高兴之余,我们突然想到如果还有其他格式的数据文件怎么办?再添加其他相应的方法吗?我们这样做不是违反了OCP(开闭原则)了吗?到这里也许有人会说 “我们可以不断在原有基础上添加新的function去对应新的change,方便简单啊!”(这样的人肯定不会是少数)。但是,我们要做一个懂得思考的programmmer,一个追求程序设计美感的programmer。所以,当我可以预见到未来可能的变化,我们就需要封装变化。

  

  How


  如何封装变化,我们首先需要知道怎么样做才是封装变化。这里我想到了ICP(依赖倒置),ICP的大意是我们需要依赖抽象而不要依赖具体。因为具体是变化的,而抽象是稳定的,所以如何封装变化我觉得就是需要用抽象的东西去封装具体的东西。对于我们上面的这个例子,抽象部分是我们的程序需要具有读取配置文件的能力,而具体的是如何读取配置文件。了解到这一点,我们的解决方案也就呼之欲出了:

 

  • 我们需要抽象程序读取配置文件的能力,这里我们可以定义这样一个接口IConfigurationReader,然后提供一个抽象方法Reader具有读取配置文件的功能
  • 接着我们需要针对不同的数据格式文件,对这个接口IConfigurationReader做相应的实现
  • 最后只需要让我们的Configuration直接去调用这个接口IConfigurationReader。针对不同格式的数据文件我们只需要给这个IConfigurationReader提供相应的实现就好了,至于具体是通过DI(依赖注入)还是其他什么方式实现这个功能,我们可以以后在考虑。

      这里我想引用一个大牛的原话“虽然软件开发技术日新月异,但是软件开发中需要解决的问题却一直没有什么变化”。同样,对于软件设计中的变化点也是如此。对此,设计模式这本书已经给了我们很好解释。设计模式这本书将设计模式分为三类,创建型模式,结构型模式和行为型模式。而当我们仔细去思考这些模式,就会发现。例如,Factory Method封装了创建时的变化,Bridge封装了结构的变化,而Visitor封装了行为的变化。所以我认为设计模式就是一本教你封装变化的书。

   
  后记,对于如何做一个好的封装。我自己也是在学习之中,套句俗话“没有最好的封装,只有更好的封装”

    

  

posted on 2011-01-28 11:53  Fei He  阅读(281)  评论(2编辑  收藏  举报