通过泛型与多态性重用代码

时间:2010-02-23 21:16:13

标签: c# generics polymorphism

重用实现的更好方法是什么:继承还是泛型?

模型如下:脚本有步骤,步骤有元素。树结构是双重链接的,即步骤知道他们的脚本和元素现在是他们的步骤。

现在,有两种类型的脚本:模板和运行,其中首先创建一个Run作为模板的副本。这导致2个类似的层次结构ScriptTemplate-> ScriptTemplateStep-> ScriptTemplateElement和ScriptRun-> ScriptRunStep-> ScriptRunElement。大多数功能都很常见,但是各种类可能都有一些额外的属性。

为了重用功能,我可以开发抽象的Script类,它将由ScriptRun和ScriptTemplate派生,如:

abstract class Script { IList<Step> Steps; }
class ScriptRun : Script {}
class ScriptTemplate : Script {}

class Step { Script Script; IList<Element> Elements; }
class ScriptRunStep : Step {}
class ScriptTemplateStep : Step {}

或者我可以尝试泛型:

abstract class Script<TScript, TStep, TElement> 
where TScript:Script<TScript, TStep, TElement>
where TStep:Step<TScript, TStep, TElement> 
where TElement:Element<TScript, TStep, TElement>
{ IList<TStep> Steps; }

abstract class Step<TScript, TStep, TElement> 
where TScript:Script<TScript, TStep, TElement>
where TStep:Step<TScript, TStep, TElement> 
where TElement:Element<TScript, TStep, TElement>
{ TScript Script; IList<TElement> Elements; }

class ScriptRun : Script<ScriptRun, ScriptRunStep, ScriptRunElement> {}
class ScriptRunStep : Step<ScriptRun, ScriptRunStep, ScriptRunElement> {}
class ScriptRunElement : Element<ScriptRun, ScriptRunStep, ScriptRunElement> {}

class ScriptTemplate : Script<ScriptTemplate, ScriptTemplateStep, ScriptTemplateElement> {}
class ScriptTemplateStep : Step<ScriptTemplate, ScriptTemplateStep, ScriptTemplateElement> {}
class ScriptTemplateElement : Element<ScriptTemplate, ScriptTemplateStep, ScriptTemplateElement> {}

仿制药方法的缺点:

  1. 起初看起来有点复杂。特别是很糟糕。
  2. 一开始似乎不熟悉。
  3. 在DataContractSerializing它时带来一些乐趣。
  4. 装配更大。
  5. 专业人士:

    1. 类型安全性:您将无法将ScriptTemplateElement添加到ScriptRunStep。
    2. 不需要从集合项目中强制转换为具体类型。此外 - 更好的intellisense支持。 ScriptTemplate.Steps立即是ScriptTemplateStep,而不是抽象Step。
    3. 遵守Liskov原则:在继承场景中,你在ScriptRun上有IList集合,但你真的不应该向它添加ScriptTemplateStep,尽管它显然是一个步骤。
    4. 您无需进行覆盖。例如。假设您想在脚本上使用NewStep方法。在前一种情况中,你说
    5. abstract class Script { abstract Step NewStep(); }
      
      abstract class ScriptRun { 
          override Step NewStep(){ 
              var step = new ScriptRunStep(); 
              this.Steps.Add(step); 
              return step; 
          } 
      }
      
      abstract class ScriptTemplate { 
          override Step NewStep(){ 
              var step = new ScriptTemplateStep(); 
              this.Steps.Add(step); 
              return step; 
          } 
      }
      

      在泛型场景中,您写道:

      abstract class Script<TScript, TStep, TElement> 
      where TScript:Script<TScript, TStep, TElement>
      where TStep:Step<TScript, TStep, TElement>, new()
      where TElement:Element<TScript, TStep, TElement>
      { 
          TStep NewStep() {
              var step = new TStep();
              this.Steps.Add(step);
              return step;
          }
      }
      

      并且ScriptRun和ScriptTemplate自动拥有该方法,或者更好的方法:返回类型分别为ScriptRunStep和ScriptTemplateStep,而不仅仅是Step。

3 个答案:

答案 0 :(得分:2)

我发现泛型通过通用属性来促进组合,而不必为要利用的每个组合编写不同的类,或者必须创建冗长的继承树。我尽力支持组合继承,特别是在单继承平台上。

我说你的情况需要两者兼顾。也许类似于以下内容:

class Child<TParent> { TParent Parent; }
class Parent<TChild> { IList<TChild> Children; }
class ParentAndChild<TParent, TChild> : Parent<TChild> { TParent Parent; }

class Element : Child<Step> { ... }
class Step : ParentAndChild<Script, Element> { ... }
class Script : Parent<Step> { ... }

这样的东西可以促进双链接对象层次结构中的大部分功能。

答案 1 :(得分:1)

都不是。重用实现的最佳方法是聚合。

之后,如果模板是可应用的,则取决于问题。我的经验法则是,产生最少量代码的任何东西都会更好。至少,那是我的经历。

[编辑] 对于这样一个精心设计的问题,这可能是一个简短的答案。

但是,特别针对这种情况(看起来主要是关于重用父子层次结构的逻辑,你可以写

 IChildOf<TPARENT> 
 { TPARENT Parent {get; set;} }


 sealed class Children<TPARENT,TCHILD> : where TCHILD: IChildOf<TPARENT>
 {
      // .... details omitted 
      public void Add(TCHILD child)
      { 
          // add child to internal collection, then:

          child.Parent = parent;
      }

      readonly TPARENT parent;
 }

TPARENT将有一个孩子&lt;&gt;集合作为成员,并将指针传递给自己。该集合确保每个TCHILD都有对其父级的引用。

答案 2 :(得分:0)

查看您的优缺点列表 - 我怀疑您已经知道了答案。缺点通常是“我不熟悉这个”,“它不那么有效”,而专业人员的形式是“它在我的代码上强制执行更严格的结构”。我说,总是选择使代码更健壮的那个,这是泛型。