My Opera is closing 3rd of March

对于Webus的重新理解

一切源于引擎

关于抽象类( abstract class )和接口( interface )的理解

1. 相似点
2. 不同点
3. 如何使用抽象类和接口
4. 综合例子

在c#, java等高级开发语言中, 都提供了抽象类和接口这两种概念. 从表面上看, 它们有如下相似点:
1. 相同的应用方式( 继承 )
2. 都起到了隐藏类型的作用
3. 都可以作为模块功能扩充的基础( 抽象类的子类或者接口的实现类可以有无限多个, 每一个都可以作为模块功能的一种扩充 )

尽管有这些相似点, 我们在设计时还是需要对抽象类和接口的应用范围加以区分, 因为它们还有如下不同的地方:
1. 抽象类是类, 而接口仅仅是类的公共成员的声明. 因此抽象类可以包含功能定义和实现, 而接口却只能够包含功能定义.
2. 抽象类是从一系列相关对象中抽象出来的概念, 因此反映的是事物的内部共性; 接口着眼于需求, 是为了满足外部调用而定义的一个功能约定, 因此反映的是事物的外部特性.

我认为应该按照下面的规则来使用抽象类和接口:
1. 分析对象, 提炼内部共性并生成抽象类
2. 用抽象类来确定对象本质, 即"是什么"( is a ). 在c#和java中, 都规定当前类只能够继承一个父类, 我想这也进一步明确了当前类到底是什么的问题
3. 当模块功能将要提供为外部模块调用时, 我们应该设计一个接口
4. 如果当前模块的功能需要扩充, 我们优先考虑接口

最后用一个小例子来说明以上观点:
假设要为一个电视厂家开发一套软件系统, 那么我们需要为电视机设计一个类:
abstract class TVBase
{
  public virtual void TurnOn() {...}
  public virtual void TurnOff() {...}
  public virtual void ChangeChannel() {...}
  public abstract void ChangeMode();
}

如果厂家生产的TV分为普通屏幕和宽屏的, 普通屏幕没有特殊功能, 而宽屏可以在普通屏幕和宽屏两种显示比例之间切换. 那么我们应该这样表示:
class TVNormal : TVBase
{
  public override void ChangeMode() {...};
}
interface IWideScreen
{
  void SetWideProportion(bool);
}
class TVWideScreen : TVBase , IWideScreen
{
  public override void ChangeMode() {...}
  public void SetWideProportion(bool isWide) {...}
}

从上述代码我们可以了解以下信息:
TVNormal是电视( class TVNormal : TVBase ); TVWideScreen也是电视, 但是是宽屏的( class TVWideScreen : TVBase, IWideScreen ); 宽屏具备切换显示比例的功能.
为了提高市场竞争力, 电视机厂家又为宽屏电视添加了录像功能, 于是我们也为录像功能设计了新接口:
interface IRecord
{
  void StartREC();
  void PauseREC();
  void StopREC();
  void PlayREC();
}

并修改了TVWideScreen的实现以满足录像功能:
class TVWideScreen : TVBase, IWideScreen, IRecord
{
  public override void ChangeMode() {...}
  public void SetWideProportion(bool isWide) {...}
  public void StartREC() {...}
  public void PauseREC() {...}
  public void StopREC() {...}
  public void PlayREC() {...}
}

宽屏电视有了录像功能之后, 市场反应十分热烈, 于是电视机厂家决定让普通电视也具备录像功能. 呵呵, 修改随之而来:
class TVNormal : TVBase, IRecord
{
  public override void ChangeMode() {...}
  public void StartREC() {...}
  public void PauseREC() {...}
  public void StopREC() {...}
  public void PlayREC() {...}
}

不过细心的你可能已经发现, 不管是宽屏电视还是普通电视, 能够录像已经成为标准配置, 仔细斟酌今后可能发生的需求, 我们认为这个厂家未来生产的电视机都会具备录像功能, 于是我们决定进行重大修改, 将录像这个共性抽象出来:
abstract class TVBase : IRecord
{
  public virtual void TurnOn() {...}
  public virtual void TurnOff() {...}
  public virtual void ChangeChannel() {...}
  public abstract void ChangeMode();
  public abstract void StartREC();
  public abstract void PauseREC();
  public abstract void StopREC();
  public abstract void PlayREC();
}

相应的, 我们修改一下TVNormal和TVWideScreen的实现:
class TVNormal : TVBase
{
  public override void ChangeMode() {...}
  public override void StartREC() {...}
  public override void PauseREC() {...}
  public override void StopREC() {...}
  public override void PlayREC() {...}
}
class TVWideScreen : TVBase, IWideScreen
{
  public override void ChangeMode() {...}
  public void SetWideProportion(bool isWide) {...}
  public override void StartREC() {...}
  public override void PauseREC() {...}
  public override void StopREC() {...}
  public override void PlayREC() {...}
}

更加明智的做法是我们将录像功能做成单独的模块, 然后在TVBase的抽象基类中去引用这个功能就行了:
class Record : IRecord
{
  public void StartREC() {...}
  public void PauseREC() {...}
  public void StopREC() {...}
  public void PlayREC() {...}
}
abstract class TVBase
{
  public virtual void TurnOn() {...}
  public virtual void TurnOff() {...}
  public virtual void ChangeChannel() {...}
  public abstract void ChangeMode();
  public Record Recorder;
}

这样处理之后, 子类无需做任何改动就具备了录像功能. 我们需要的灵活性和可维护性也就展现处来了 ^_^

Webus的背景故事

Write a comment

New comments have been disabled for this post.

March 2014
S M T W T F S
February 2014April 2014
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