【问题标题】:MFC Best practice and modularityMFC 最佳实践和模块化
【发布时间】:2012-10-30 03:28:03
【问题描述】:

在 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
     }
};

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

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 data structures tend to carry their own operators around with them"的概念被破坏了..对吧?

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

【问题讨论】:

  • 这个问题并不是针对stackoverflow的,因为它实际上只是在征求意见。您可以选择如何使用 MFC。而倾向于这个词并不意味着总是。
  • 抱歉,我没有意识到我不被允许提出引发辩论的问题..
  • 所有意见都是有效的,大部分都是不同程度的错误,因为它们都是简化的。这包括我刚刚写的这个观点。
  • 我将此解释为关于模块化而不是 MFC 使用的问题。当然,我的解释可能是错误的......
  • 这个问题涉及 Windows 消息传递体系结构以及 Windows 消息如何在各个父窗口和子窗口之间传播。 MFC 是底层平台上的皮肤,结果是有很多方法可以在皮肤非常薄且骨骼显露的情况下缩短 MFC。提问者已经知道 MFC 将 DrawItem 功能封装到正在绘图的窗口中的正确主流使用。另一方面,有时在父级中进行绘图是一种有效的方法,尽管我现在想不出。

标签: c++ oop winapi modularity


【解决方案1】:

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

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

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

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

请参阅此article in wikipedia on the Strategy pattern 或此article on Strategy pattern with side links for other patterns,它提供了有关此类编程问题的简要讨论。另一个例子可能是Chain of Responsibility pattern

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

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

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

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 那不接近。父级使用 ID 来区分其子控件,并在创建控件时在父级代码中分配。按钮本身并不关心实际值。 ID 是 GoF Observer 模式中的“Observable”对象引用,没有比这更糟糕的了。
    • 汉斯,什么是 GoF 观察者模式?我现在很感兴趣:)
    • @HansPassant 抱歉,我在不了解 MFC 内部如何工作的情况下笼统地谈论模块化。如果人们想要我,我会删除我的答案。像 MFC 这样的 GUI 框架会使用 Observer(以及其他模式)是有道理的。
    • @ryan0,你为什么不先尝试一下。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-10
    • 2016-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多