2011-11-26 50 views
2

我一直在尝试将正确的OOP原则应用于我的项目。我有一个名为DocumentSection的抽象类,以及从它派生的几个类(DocumentSectionView,DocumentSectionText等)。同样,我有一个抽象类(DocAction),它有几个派生自它的类(DocumentActionReplaceByTag,DocumentSectionAppend等)。每个DocumentSection都有一个DocumentAction。派生类中属性的可见性(C#)

我对所有这些继承业务的理解是,通过指定一个'DocumentAction',这将允许任何这些派生类放在它的位置,并且基类中的任何属性/方法都可用,例如以及我在实例化的具体类中指定的任何东西。因此,在下面的例子中,我希望能够看到PerformAction方法(现在将虚拟/覆盖关键字留在混合中)。它是可用的。然而,因为我去了v.DocAction = new DocumentActionReplaceByTag(),所以我还希望我的ReplaceActionFindText属性可见。

很明显,我错了某处 - 任何意见赞赏。

class Program 
{ 
    static void Main(string[] args) 
    { 
     DocumentSectionView v = new DocumentSectionView(); 
     v.DocAction = new DocumentActionReplaceByTag(); 

     // would like to go: 
     //v.DocAction.ReplaceActionFindText... 

     Console.ReadLine(); 
    } 
}  
public abstract class DocumentSection 
{ 
    public abstract string GetContent(); 
    public DocumentAction DocAction { get; set; } 
} 
public class DocumentSectionView : DocumentSection 
{ 
    public string ViewPath { get; set; } 
    public dynamic ViewModel { get; set; } 

    public override string GetContent() 
    { 
     return "test"; 
    } 
}  
public abstract class DocumentAction 
{ 
    void PerformAction(StringBuilder sb, string content); 
} 
public class DocumentActionReplaceByTag : DocumentAction 
{ 
    public string ReplaceActionFindText { get; set; } 
    public void PerformAction(StringBuilder sb, string content) 
    { 
     sb.Replace(ReplaceActionFindText, content); 
    } 
} 

编辑: 我标志着一个答案是正确的,但想到我会加上我进一步思考的成果,在这个问题上对那些跨稍后即将到来:

一)正如指出的,我的意图是大致正确的,但我的方法是错误的。从Main方法设置Action的属性是不正确的。在所有情况下,AA DocumentActionReplaceByTag需要FINDTEXT,所以我把它在构造函数中:

public DocumentActionReplaceByTag(string replaceActionFindText) 
    { 
     this.ReplaceActionFindText = replaceActionFindText; 
    } 

从此,0参数的构造函数会正确地失败,并防止在执行该操作的情况下,但没有FINDTEXT是指定。

b)多态现在工作正常,因为我的额外属性findtext已填充,并且运行PerformAction将正确运行,无论操作类型如何。

+0

谢谢大家。那么,在DocumentSection中指定任何类型的DocAction都可以指定的'OOP正确'方式是什么。我们的目标是从我的旧方法(在http://stackoverflow.com/questions/8242520/correct-oop-practice-for-class-properties-tied-by-logic讨论)移动到使用多态调用action.GetContent ();在我的DocAction上运行任何适当的getcontent动作。但要做到这一点,我需要设置特定于该操作的属性(如ReplaceActionText)。下面的演员被评论为不理想 - 正在铸造唯一的方法? – Glinkot

回答

2

因为您将派生类分配给具有基类类型的属性,所以只有基类的方法和属性可用。这是有道理的,因为你可能已经分配了任何从基类派生的类的实例 - 所以任何派生的方法都不能在此上下文中使用。

这是对OOP原则之一 - 你的派生类的实例可以作为基类的一个实例(而不是其他方式轮)

编辑:

阐述一下解决方案由@sll建议转换为特定的派生类类型:不要这样做!这是一种解决方法,但不符合整体设计的利益。

如果您必须转换为派生类型,那么您违反了Liskov substitution principle这意味着任何派生类型都应该可用来代替基类型 - 如果您需要特定演员阵列,则显然不是这种情况。

重新考虑你的设计 - 你真的需要与基类类型的属性,如果是这样的方法,目前只在一个特定的派生类型更好的基本类型是呢?

+0

谢谢Brokenglass。在我上面放链接(http://stackoverflow.com/questions/8242520/correct-oop-practice-for-class-properties-tied-by-logic)我试图避免这并不涉及到其性质一个特定的目的。例如,一个动作可能需要路径/文件名,而另一个动作可能需要“textcontent”属性。将这两种绑定到基类听起来不像“正确的方式”?感谢 – Glinkot

0

不,在你的例子中,因为DocAction只是一个DocumentAction,你将只能看到DocumentAction的属性,无论使用哪种派生类型DocumentAction

2

v引用类型是DocumentSectionView,它不知道DocumentActionReplaceByTag类的方法,即使您已经为其分配底层实例的DocumentActionReplaceByTag类。你需要投它能够accesing派生类中的成员:

((DocumentActionReplaceByTag)v.DocAction).ReplaceActionFindText 
在某些情况下,这是相当正常,当底层实例无法铸造这样的代码部分资金应该被跳过,那么你可以使用exception-

而且使用as operator铸造的安全方式:

var typedAction = v.DocAction as DocumentActionReplaceByTag; 
if (typedAction != null) 
{ 
    // accessing the typedAction.ReplaceActionFindText property 
} 

我的建议是只帮助你理解问题的C#的一面,关于整体设计和方法,请参阅BrokenGlass的答案。

+1

+1指出铸造都会让他做他想做 –

+1

这会损害这种设计的目的虽然 – BrokenGlass

+0

@BrokenGlass什么:对不起,我没有得到一点关于目前的设计,你可以澄清吗?这种设计的正确选择是什么?我刚才看到的方法是代码'主()'不分配派生类实例时基参考 – sll