钻石继承 (C++)

2024-02-25

我知道拥有钻石继承被认为是不好的做法。然而,我有两个案例,我觉得钻石继承非常适合。我想问,在这些情况下你会建议我使用钻石继承,还是有其他设计可以更好。

Case 1:我想创建代表系统中不同类型“操作”的类。这些动作按几个参数分类:

  • 该操作可以是“读”或“写”。
  • 该操作可以有延迟或没有延迟(它不仅仅是 1 个参数。它会显着改变行为)。
  • 操作的“流类型”可以是 FlowA 或 FlowB。

我打算有以下设计:

// abstract classes
class Action  
{
    // methods relevant for all actions
};
class ActionRead      : public virtual Action  
{
    // methods related to reading
};
class ActionWrite     : public virtual Action  
{
    // methods related to writing
};
class ActionWithDelay : public virtual Action  
{
    // methods related to delay definition and handling
};
class ActionNoDelay   : public virtual Action  {/*...*/};
class ActionFlowA     : public virtual Action  {/*...*/};
class ActionFlowB     : public virtual Action  {/*...*/};

// concrete classes
class ActionFlowAReadWithDelay  : public ActionFlowA, public ActionRead, public ActionWithDelay  
{
    // implementation of the full flow of a read command with delay that does Flow A.
};
class ActionFlowBReadWithDelay  : public ActionFlowB, public ActionRead, public ActionWithDelay  {/*...*/};
//...

当然,我会遵守任何两个动作(继承自 Action 类)都不会实现相同的方法。

Case 2:我在我的系统中实现了“命令”的复合设计模式。一个命令可以读取、写入、删除等。我还希望有一个命令序列,也可以读取、写入、删除等。一个命令序列可以包含其他命令序列。

所以我有如下的设计:

class CommandAbstraction
{
    CommandAbstraction(){};
    ~CommandAbstraction()=0;
    void Read()=0;
    void Write()=0;
    void Restore()=0;
    bool IsWritten() {/*implemented*/};
    // and other implemented functions
};

class OneCommand : public virtual CommandAbstraction
{
    // implement Read, Write, Restore
};

class CompositeCommand : public virtual CommandAbstraction
{
    // implement Read, Write, Restore
};

此外,我还有一种特殊的命令,“现代”命令。单一命令和复合命令都可以是现代命令。 “现代”向一个命令和复合命令添加了一系列属性(这两个命令的属性大多相同)。我希望能够保存一个指向 CommandAbstraction 的指针,并根据所需的命令类型对其进行初始化(通过 new )。所以我想做以下设计(除了上面的之外):

class ModernCommand : public virtual CommandAbstraction
{
    ~ModernCommand()=0;
    void SetModernPropertyA(){/*...*/}
    void ExecModernSomething(){/*...*/}
    void ModernSomethingElse()=0;

};
class OneModernCommand : public OneCommand, public ModernCommand
{
    void ModernSomethingElse() {/*...*/};
    // ... few methods specific for OneModernCommand
};
class CompositeModernCommand : public CompositeCommand, public ModernCommand
{
    void ModernSomethingElse() {/*...*/};
    // ... few methods specific for CompositeModernCommand
};

再次,我将确保从 CommandAbstraction 类继承的两个类不会实现相同的方法。

谢谢。


继承是 C++ 中第二强(耦合性更强)的关系,仅次于友谊。如果您可以重新设计为仅使用组合,您的代码将更加松散耦合。如果不能,那么您应该考虑所有的类是否都应该真正从基类继承。是由于实现还是只是一个接口?您想要使用层次结构的任何元素作为基本元素吗?或者只是层次结构中的叶子才是真正的操作?如果只有叶子是操作,并且您要添加行为,则可以考虑针对此类行为组合进行基于策略的设计。

这个想法是,可以在小类集中定义不同的(正交)行为,然后将其捆绑在一起以提供真正的完整行为。在示例中,我将仅考虑一项策略,该策略定义是现在执行还是将来执行操作,以及要执行的命令。

我提供了一个抽象类,以便模板的不同实例可以存储(通过指针)在容器中或作为参数传递给函数并被多态调用。

class ActionDelayPolicy_NoWait;

class ActionBase // Only needed if you want to use polymorphically different actions
{
public:
    virtual ~Action() {}
    virtual void run() = 0;
};

template < typename Command, typename DelayPolicy = ActionDelayPolicy_NoWait >
class Action : public DelayPolicy, public Command
{
public:
   virtual run() {
      DelayPolicy::wait(); // inherit wait from DelayPolicy
      Command::execute();  // inherit command to execute
   }
};

// Real executed code can be written once (for each action to execute)
class CommandSalute
{
public:
   void execute() { std::cout << "Hi!" << std::endl; }
};

class CommandSmile
{
public:
   void execute() { std::cout << ":)" << std::endl; }
};

// And waiting behaviors can be defined separatedly:
class ActionDelayPolicy_NoWait
{
public:
   void wait() const {}
};

// Note that as Action inherits from the policy, the public methods (if required)
// will be publicly available at the place of instantiation
class ActionDelayPolicy_WaitSeconds
{
public:
   ActionDelayPolicy_WaitSeconds() : seconds_( 0 ) {}
   void wait() const { sleep( seconds_ ); }
   void wait_period( int seconds ) { seconds_ = seconds; }
   int wait_period() const { return seconds_; }
private:
   int seconds_;
};

// Polimorphically execute the action
void execute_action( Action& action )
{
   action.run();
}

// Now the usage:
int main()
{
   Action< CommandSalute > salute_now;
   execute_action( salute_now );

   Action< CommandSmile, ActionDelayPolicy_WaitSeconds > smile_later;
   smile_later.wait_period( 100 ); // Accessible from the wait policy through inheritance
   execute_action( smile_later );
}

使用继承允许通过模板实例化来访问策略实现中的公共方法。这不允许使用聚合来组合策略,因为不能将新函数成员推送到类接口中。在示例中,模板取决于具有 wait() 方法的策略,该方法对于所有等待策略都是通用的。现在等待一个时间段需要一个固定的时间段,通过 period() 公共方法设置。

在示例中,NoWait 策略只是 WaitSeconds 策略的一个特定示例,其周期设置为 0。这是有意标记策略接口不需要相同。另一种等待策略实现可以通过提供一个注册为给定事件回调的类来等待一定数量的毫秒、时钟滴答或直到某个外部事件。

如果您不需要多态性,您可以从示例中取出基类和虚拟方法。虽然对于当前示例来说这似乎过于复杂,但您可以决定添加其他策略。

虽然如果使用普通继承(具有多态性),添加新的正交行为将意味着类数量呈指数级增长,但使用这种方法,您可以单独实现每个不同的部分,并将其在 Action 模板中粘合在一起。

例如,您可以使操作定期进行,并添加退出策略来确定何时退出定期循环。首先想到的选项是 LoopPolicy_NRuns 和 LoopPolicy_TimeSpan、LoopPolicy_Until。这个策略方法(在我的例子中是 exit() )为每个循环调用一次。第一个实现对固定次数(由用户固定,如上例中的周期固定)后被调用退出的次数进行计数。第二种实现将在给定时间段内定期运行该进程,而最后一个实现将运行该进程直到给定时间(时钟)。

如果你还关注我到这里,我确实会做出一些改变。第一个是,我不使用实现方法execute() 的模板参数Command,而是使用仿函数以及可能使用要执行的命令作为参数的模板化构造函数。理由是,这将使其与其他库(如 boost::bind 或 boost::lambda)结合起来更具可扩展性,因为在这种情况下,命令可以在实例化时绑定到任何自由函数、函子或成员方法一个类的。

现在我得走了,但如果你有兴趣,我可以尝试发布修改后的版本。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

钻石继承 (C++) 的相关文章

  • 访问私人成员[关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 通过将类的私有成员转换为 void 指针 然后转换为结构来访问类的私有成员是否合适 我认为我无权修改包含我需要访问的数据成员的类 如果不道德 我
  • C# 和 Javascript SHA256 哈希的代码示例

    我有一个在服务器端运行的 C 算法 它对 Base64 编码的字符串进行哈希处理 byte salt Convert FromBase64String serverSalt Step 1 SHA256Managed sha256 new S
  • Qt-Qlist 检查包含自定义类

    有没有办法覆盖加载自定义类的 Qt QList 的比较机制 即在 java 中你只需要重写一个比较方法 我有一个带有我的自定义类模型的 QList QList
  • 如何使用GDB修改内存内容?

    我知道我们可以使用几个命令来访问和读取内存 例如 print p x 但是如何更改任何特定位置的内存内容 在 GDB 中调试时 最简单的是设置程序变量 参见GDB 分配 http sourceware org gdb current onl
  • 如何忽略“有符号和无符号整数表达式之间的比较”?

    谁能告诉我必须使用哪个标志才能使 gcc 忽略 有符号和无符号整数表达式之间的比较 警告消息 gcc Wno sign compare 但你确实应该修复它警告你的比较
  • 使闭包捕获的变量变得易失性

    闭包捕获的变量如何与不同线程交互 在下面的示例代码中 我想将totalEvents 声明为易失性的 但C 不允许这样做 是的 我知道这是错误的代码 这只是一个例子 private void WaitFor10Events volatile
  • 当 contains() 工作正常时,xpath 函数ends-with() 工作时出现问题

    我正在尝试获取具有以特定 id 结尾的属性的标签 like span 我想获取 id 以 国家 地区 结尾的跨度我尝试以下xpath span ends with id Country 但我得到以下异常 需要命名空间管理器或 XsltCon
  • 为什么#pragma optimize("", off)

    我正在审查一个 C MFC 项目 在某些文件的开头有这样一行 pragma optimize off 我知道这会关闭所有以下功能的优化 但这样做的动机通常是什么 我专门使用它来在一组特定代码中获得更好的调试信息 并在优化的情况下编译应用程序
  • 获取没有非标准端口的原始 url (C#)

    第一个问题 环境 MVC C AppHarbor Problem 我正在调用 openid 提供商 并根据域生成绝对回调 url 在我的本地机器上 如果我点击的话 效果很好http localhost 12345 login Request
  • Web API - 访问 DbContext 类中的 HttpContext

    在我的 C Web API 应用程序中 我添加了CreatedDate and CreatedBy所有表中的列 现在 每当在任何表中添加新记录时 我想填充这些列 为此目的我已经覆盖SaveChanges and SaveChangesAsy
  • 在 ASP.NET Core 3.1 中使用包含“System.Web.HttpContext”的旧项目

    我们有一些用 Net Framework编写的遗留项目 应该由由ASP NET Core3 1编写的API项目使用 问题是这些遗留项目正在使用 System Web HttpContext 您知道它不再存在于 net core 中 现在我们
  • for循环中计数器变量的范围是多少?

    我在 Visual Studio 2008 中收到以下错误 Error 1 A local variable named i cannot be declared in this scope because it would give a
  • 如何将单个 char 转换为 int [重复]

    这个问题在这里已经有答案了 我有一串数字 例如 123456789 我需要提取它们中的每一个以在计算中使用它们 我当然可以通过索引访问每个字符 但是如何将其转换为 int 我研究过 atoi 但它需要一个字符串作为参数 因此 我必须将每个字
  • Qt表格小部件,删除行的按钮

    我有一个 QTableWidget 对于所有行 我将一列的 setCellWidget 设置为按钮 我想将此按钮连接到删除该行的函数 我尝试了这段代码 它不起作用 因为如果我只是单击按钮 我不会将当前行设置为按钮的行 ui gt table
  • 如何在 VBA 中声明接受 XlfOper (LPXLOPER) 类型参数的函数?

    我在之前的回答里发现了问题 https stackoverflow com q 19325258 159684一种无需注册即可调用 C xll 中定义的函数的方法 我之前使用 XLW 提供的注册基础结构 并且使用 XlfOper 类型在 V
  • 防止Java实例化的正确方法[关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 为什么 C# Math.Ceiling 向下舍入?

    我今天过得很艰难 但有些事情不太对劲 在我的 C 代码中 我有这样的内容 Math Ceiling decimal this TotalRecordCount this PageSize Where int TotalRecordCount
  • Process.Start 阻塞

    我正在调用 Process Start 但它会阻止当前线程 pInfo new ProcessStartInfo C Windows notepad exe Start process mProcess new Process mProce
  • Validation.ErrorTemplate 的 Wpf 动态资源查找

    在我的 App xaml 中 我定义了一个资源Validation ErrorTemplate 这取决于动态BorderBrush资源 我打算定义独特的BorderBrush在我拥有的每个窗口以及窗口内的不同块内
  • 防止索引超出范围错误

    我想编写对某些条件的检查 而不必使用 try catch 并且我想避免出现 Index Out of Range 错误的可能性 if array Element 0 Object Length gt 0 array Element 1 Ob

随机推荐

  • 如何从 scrapy 蜘蛛回调中收集统计信息?

    如何从蜘蛛回调中收集统计数据 Example class MySpider Spider name myspider start urls http example com def parse self response stats set
  • C++11:重要的线程局部静态变量?

    我有一个X类 class X 我想做这个 void f thread local static X x 实际上我使用的是 gcc 所以关键字是 thread 但我不能 因为你只能有微不足道的 thread locals 对此最好的解决方法是
  • 将 lmer 输出到 word/excel

    我正在工作R 有一个名为lme4 执行模型 lmer rasch lt lmer Response item 1 1 STIDSTD family binomial data exampledata 让我在控制台中输出 如帖子末尾所示 我想
  • 确定无冲突集?

    假设您有一堆集合 而每个集合都有几个子集 Set1 香蕉 菠萝 橙子 苹果 羽衣甘蓝 黄瓜 洋葱 大蒜 Set2 香蕉 黄瓜 大蒜 鳄梨 番茄 设置N 现在的目标是从每个集合中选择一个子集 而每个子集必须与任何其他选定的子集不发生冲突 对于
  • Coffeescript、Backbone 和加载顺序

    考虑这个 Coffeescript 类 在一个应用程序中 每个类都位于自己的文件中 class Manager extends Person title titles manager 如果该文件在 title 对象之前加载 则会生成错误 我
  • JavaScript 对象作为函数参数

    使用 JavaScript 假设我有一个函数 X 并且在该函数中创建了一个名为 objectX 的对象 函数 X 返回对象 X 稍后在代码中 函数 Z somevar anObject 接收 objectX 作为其参数之一 现在在函数 Z
  • 索引键列 VS 索引包含列

    谁能解释一下这两个 索引Key列 VS 索引Included Column 目前 我的索引有 4 个索引键列和 0 个包含列 我想知道两者之间的区别 索引键列是索引 B 树的一部分 包含的列不是 取两个索引 CREATE INDEX ind
  • Codesandbox.io 中的 React-Testing-Library 问题

    我对 React 来说是一个相对菜鸟 想在这个沙盒中练习一些 tdd基本反应沙箱 https codesandbox io s 93yp98knx4 测试代码似乎正在影响应用程序组件状态 如果您在浏览器和测试之间切换 您将看到测试项目被添加
  • SQL Select 包括数据类型和数据值

    如何从表中选择数据并将数据类型包含在值中 例如 我有一个名为 EMPLOYEE 的表 其中包含 fname lastname phone 等 如果我做 SELECT FROM EMPLOYEE 我得到了所有的列 如果我做 SELECT CO
  • 如何定位 UIView?

    我花了几个小时尝试定位 UIView 最终发现我需要修改视图框架 所以我向 UIViewController 子类添加了一个 setPosition 方法 如下所示 void setPosition CGPoint position CGR
  • 将 YYYYMMDD 字符串日期转换为日期时间值[重复]

    这个问题在这里已经有答案了 可能的重复 在 C 中将字符串转换为日期时间 https stackoverflow com questions 1592653 convert string to datetime in c sharp 一个问
  • UIViewController PresentViewController 在 ios 8 上崩溃

    刚刚在我们的错误记录系统中收到此错误 一直在高低搜索 似乎找不到任何解决方案 任何帮助表示赞赏 这里是堆栈跟踪 https gist github com hermanccw 2a9264fd77d46e0e7279 可能在您的 XCODE
  • RESTful 破坏 Rails 中的多态关联?

    如何销毁关联本身并保留关联的对象 同时保持 RESTful 具体来说 我有这些模型 class Event lt ActiveRecord Base has many model surveys as gt surveyable depen
  • 对于表单中的每个控件,如何对表单中的所有文本框执行某些操作?

    如何使用 Foreach 语句对我的文本框执行某些操作 foreach Control X in this Controls Check if the controls is a TextBox if it is delete it s T
  • 命名空间 system.windows 中不存在“Forms”

    我刚刚开始研究 c 并且正在摆弄从某个论坛获得的一些代码示例 此代码使用命名空间using system windows forms我收到错误 命名空间 system windows 中不存在 Forms 我还收到一些与未定义函数相关的错误
  • 如何使用 Azure 移动服务 API 功能

    An APIWAMS 中添加了功能 我可以在其中定义自定义脚本 这似乎反对以前创建脚本表的做法 但是 我找不到任何有关如何使用它的描述 哪些客户端可以使用此功能 可以在 iOS 或 Javascript 中使用吗 还有关于这个主题的更多帖子
  • 我可以让 valgrind 告诉我哪个值未初始化吗?

    I ran valgrind一些代码如下 valgrind tool memcheck leak check full track origins yes test 它返回以下错误 24860 Conditional jump or mov
  • 删除 Safari/Chrome 文本输入/文本区域发光

    我想知道当我使用 CSS 单击文本输入 文本区域时是否可以删除默认的蓝色和黄色发光 编辑 11 年后 不要这样做 除非您要提供后备来指示哪个元素处于活动状态 否则 这会损害可访问性 因为它本质上删除了显示文档中哪个元素具有焦点的指示 想象一
  • mysql json vs mongo - 存储空间

    我遇到了一个有趣的情况 虽然不是一个实际的问题 但我不明白为什么会发生这种情况 我们有一个 mongo 数据库 主要由存储在数组中的一些批量数据组成 由于团队中超过 90 的人熟悉 mysql 而只有少数人熟悉 mongo 再加上这不是关键
  • 钻石继承 (C++)

    我知道拥有钻石继承被认为是不好的做法 然而 我有两个案例 我觉得钻石继承非常适合 我想问 在这些情况下你会建议我使用钻石继承 还是有其他设计可以更好 Case 1 我想创建代表系统中不同类型 操作 的类 这些动作按几个参数分类 该操作可以是