0

在 MFC 中,提供的某些机制允许程序员绕过模块化和封装以及信息隐藏,这可以说是面向对象框架最理想的特性。

一个(众多)示例是 Owner Drawn 控件:您可以选择DrawItem在子控件子类中实现并在该子类中完成该控件的所有绘图,使其看起来更加模块化:

class CustomButton: CButton{
     // --- Lots of stuff, DECLARE_DYNAMIC etc

     virtual void DrawItem(LPDRAWITEMSTRUCT lpdis){
          // Drawing code for this button in the button's subclass
     }
};

...或者您可以选择WM_DRAWITEM通过 OnDrawItem 在父 Window 类中处理消息

class MainFrame: CFrameWnd{
     // --- Lots of stuff, DECLARE_DYNAMIC etc

     CustomButton button; 

     afx_msg void OnDrawItem(LPDRAWITEMSTRUCT lpdis, UINT id){

          if(id == CUSTOM_BUTTON_ID){     
               // Drawing code for this button in the button's subclass
          }
     }
};

在后一种情况下,控件的绘制在控件子类之外,这意味着“OOP 数据结构倾向于随身携带自己的运算符”的概念被破坏了.. 对吗?

所以我的问题是:哪一个被认为是“最佳实践”?第二个存在一定是有原因的——任何人都可以提出破坏模块化是更好选择的情况吗?

4

2 回答 2

1

这个问题似乎是在问什么时候让一个包含一个或多个子对象的父对象来处理用于子对象的消息而不是仅仅将消息传递给子对象让子对象处理它是合理的。

具体示例是 MFC 窗口类,其中包含其他窗口对象(在本例中为按钮)的窗口对象正在处理绘制消息,而不是仅将消息传递给子级进行处理。

基本答案是视情况而定。在某些情况下,您正在编写一个窗口对象,并且作为其中的一部分,您会超越子控件的绘图行为以更改子控件的外观。例如,如果您有一组按钮,您希望根据父窗口中的信息更改其外观,您可能希望覆盖子控件的绘图以更改外观。

这类似于拥有一个按钮对象,它通过允许父窗口向子控件提供绘图对象来允许多种不同类型的外观。结果将是子级将具有默认的绘图行为,父级将通过提供绘图对象来覆盖该行为。

请参阅维基百科中关于策略模式的这篇文章或这篇关于策略模式的文章以及其他模式的侧链接,其中提供了对此类编程问题的简要讨论。另一个例子可能是责任链模式

在这个特定的例子中,不是父级提供绘图描述对象给子级使用,而是父级只是截取绘图消息并自己进行绘图。

如果可以在不影响子控件的其他功能的情况下更改子控件的外观,那么这将是一个不错的方法,尽管它可能会使正在进行维护或其他更改的程序员感到困惑。然而,在其他情况下可以看到这种越权行为,例如窗口剪辑,其中一些窗口组件的绘图被剪辑,因此不会绘制窗口区域之外的组件的任何部分。

因此,让父对象拦截和处理以父对象的子对象为目标的消息的方法提供了很大的灵活性,允许不同的父对象根据父对象想要完成的内容来改变子对象的特定行为。

然而,这种灵活性确实是有代价的。

于 2012-10-29T18:11:48.087 回答
-1

如果在第二种情况下,CUSTOM_BUTTON_ID 是控件内部使用的一些数据,那么 OOP 将不是最理想的。这将违反 David Parnas (http://www.cs.umd.edu/class/spring2003/cmsc838p/Design/criteria.pdf) 定义的信息隐藏概念。如果内部信息发生变化,则父窗口会受到变化的影响,需要对其自身进行更改以匹配。这就是信息隐藏促进模块化的方式;它将数据与需要它的东西捆绑在一起(并对不需要的东西隐藏它)。

根据评论,这是一个不好的例子,因为没有违反信息隐藏,因为 CUSTOM_BUTTON_ID 实际上是父窗口包含和使用的数据,而不是控件。控件的绘制还是在控件内部完成的;父窗口只是调用控件的 Draw 方法。

于 2012-10-26T19:28:22.483 回答