代码之家  ›  专栏  ›  技术社区  ›  user76035

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

  •  0
  • user76035  · 技术社区  · 14 年前

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

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

    为了重用功能,我可以开发由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. 总成较大。

    优点:

    1. 类型安全性:无法将ScriptTemplateElement添加到ScriptRunStep。
    2. 遵循Liskov原则:在继承场景中,在ScriptRun上有IList集合,但确实不应该向其中添加ScriptTemplateStep,尽管这显然是一个步骤。

    :

    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,而不仅仅是一个步骤。

    3 回复  |  直到 14 年前
        1
  •  2
  •   Travis Heseman    14 年前

    我发现泛型通过泛型属性来促进组合,而不必为要利用的每个组合编写不同的类,也不必创建冗长的继承树。当我可以的时候,我会尽量支持组合继承,特别是在单一继承平台上。

    我想说你的处境需要两者兼而有之。可能是这样的:

    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> { ... }
    

    类似这样的东西可以促进双链接对象层次结构中的许多功能。

        2
  •  1
  •   user180326 user180326    14 年前

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

    [编辑] 对于这样一个复杂的问题,这也许是一个简短的回答。

     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 将有一个孩子<&燃气轮机;集合作为成员,并向其传递指向自身的指针。该系列确保 TCHILD 具有对其父级的引用。

        3
  •  0
  •   Aidan Cully    14 年前

    看看你的利弊清单-我想你已经知道答案了。缺点通常是“我不太熟悉这个”,“它没有那么有效”,而优点是“它在我的代码上强制执行更严格的结构”。我要说的是,一定要选择使代码更健壮的泛型。