C++ 如何避免复合图案中的向下投射
我使用复合模式来表示a,它描述了材料的力学行为,例如,模型,与n平行的弹簧麦克斯韦元件iteslf是一个串联有阻尼器的弹簧。 机械网络是在运行时动态组成的。为此,我从一个公共基类C++ 如何避免复合图案中的向下投射,c++,design-patterns,composite,visitor-pattern,C++,Design Patterns,Composite,Visitor Pattern,我使用复合模式来表示a,它描述了材料的力学行为,例如,模型,与n平行的弹簧麦克斯韦元件iteslf是一个串联有阻尼器的弹簧。 机械网络是在运行时动态组成的。为此,我从一个公共基类组件派生了Spring、MaxwellElement和ParallelConnection: struct Component { virtual void add_component (Component* ) { } virtual Tensor<2> stress (const Tens
组件派生了Spring
、MaxwellElement
和ParallelConnection
:
struct Component {
virtual void add_component (Component* ) { }
virtual Tensor<2> stress (const Tensor<2> &strain) const = 0;
virtual ~Component () = default;
};
struct Spring : Component {
Tensor<2> stress (const Tensor<2> &strain) const override {return {};};
};
struct SpecialSpring : Spring {
Tensor<1> principal_stresses (const Tensor<1> &strain_eigenvalues) const {return {};};
};
struct ParallelConnection : Component {
void add_component (Component* c) override {components.emplace_back(c);}
Tensor<2> stress (const Tensor<2> &strain) const override {
Tensor<2> s;
for (const auto& c : components)
s += c->stress(strain);
return s;
};
std::vector<std::unique_ptr<Component>> components;
};
struct MaxwellElement : Component {
Tensor<2> stress (const Tensor<2> &strain) const override {
auto special_spring = dynamic_cast<SpecialSpring*>(spring.get());
if (special_spring) {
const auto ps = special_spring->principal_stresses (eigenvalues(strain));
// Some cheap calculations using ps and member variables
// of MaxwellElement, which are left out for simplicity.
return {}; // <- meaningful result
} else {
const auto s = spring->stress (strain);
// Some expensive calculations using s and member variables
// of MaxwellElement, which are left out for simplicity.
return {}; // <- meaningful result
}
};
virtual void add_component (Component* c) {
if (!spring)
spring.reset(dynamic_cast<Spring*>(c));
assert (spring);
}
std::unique_ptr<Spring> spring;
};
实现组件visitor
和添加accept
方法作为家庭作业;)我希望我的目标仍然清晰。现在,基于std::variant::index()
,我可以选择正确的算法。然而,我还是觉得自己的设计很糟糕
有没有更好的方法来实现上述目标?
我当前的实现是问题本身吗?我正在处理的是XY问题吗?如果我理解正确,似乎是这样或那样,您需要以编程的方式确定要执行哪些计算—无论是便宜的还是昂贵的—问题在于如何实现这一点
假设张量
只是张量
在结构上的简化表示(因为张量通常是这样的,对吗?),我会同意在SpecialSpring
类中覆盖应力()
- 它会在内部调用
特征值(应变)
- 它将其内部计算结果概括为
张量
表示并返回该表示
大概是这样的:
struct Spring : Component {
virtual Tensor<2> stress (const Tensor<2> &strain) const override {return {};};
};
struct SpecialSpring : Spring {
Tensor<1> principal_stresses (const Tensor<1> &strain_eigenvalues) const {return {};};
virtual Tensor<2> stress (const Tensor<2> &strain) const override {
Tensor<1> t1 = principal_stress(eigenvalues(strain));
Tensor<2> t2 = some_function_to_generalize_T1_to_T2(t1);
return t2;
};
};
struct-Spring:组件{
虚张量应力(常数张量&应变)常数覆盖{return{};};
};
结构特殊弹簧:弹簧{
张量主应力(常数张量&应变特征值)常数{return{};};
虚拟张量应力(常数张量和应变)常数覆盖{
张量t1=主应力(本征值(应变));
张量t2=一些函数,将T1推广到t2(T1);
返回t2;
};
};
然后,在MaxwellElement::stress()
中,您只需调用spring->应力(应变)
,然后检查返回的张量是否足够简单,可以进行廉价的计算
当然,使用这种方法,还有一个新问题-额外的计算(将张量的表示形式更改为张量,然后在返回张量后基本上做相反的事情)是否更好(性能方面)比动态的演员阵容听起来有点像解决问题的方法。他们早已被提议用于C++,但从未将其应用到语言中。看起来模式匹配将提供类似的功能,但具有不同的属性。@dyp是的,您是对的。在这种特殊情况下,多方法会很好。我没听说过模式匹配。就是你指的。是的,在某种程度上。或者类似的。想法是定义两种类型的模式(哪个类派生自组件,哪个Spring类型)。multimethods和pattern matching都不完全适合您的问题,因为在您的情况下,spring隐藏在组件内部,而且Component::stress
的调用者无法访问它进行分派;然而,我认为这里的例子是不完整的。不知何故,SpringVisitor
必须传递到组件中,也就是说,您似乎缺少Visitor模式中的accept()
方法。推送实现总是可以实现的,但它会受到紧耦合的影响。例如,在弹簧类中,张量应力(常数张量&应变)常数覆盖;麦克斯韦(/*任何必要的参数*/)=0的虚拟张量应力
maxwell的应力\u优化\u应能在普通字符串和特殊字符串上运行,但在特殊字符串上使用便宜的计算。+1,因为这种方法可能对其他方法有所帮助。然而,这在我的情况下不起作用。一阶张量张量
是向量,二阶张量张量
是矩阵。也就是说,张量
可以存储在张量
中。您的解决方案的问题是,压力(…)
的原始结果将丢失。当SpecialSpring
是ParallelConnection
中的一个组件时,它需要调用原始的应力(…)
函数。我目前的方法困扰我的并不是动态转换的性能损失(即使是便宜的计算也比这昂贵得多)更重要的是我感到沮丧。对我来说,这是我的代码设计选择不佳的标志。因此,访问者模式进行了调度。也许拥有MaxwellElement
和SpecialMaxwellElement
,将是一个解决方案。但是谢谢,我真的很感谢你的帮助。
struct Spring : Component {
virtual Tensor<2> stress (const Tensor<2> &strain) const override {return {};};
};
struct SpecialSpring : Spring {
Tensor<1> principal_stresses (const Tensor<1> &strain_eigenvalues) const {return {};};
virtual Tensor<2> stress (const Tensor<2> &strain) const override {
Tensor<1> t1 = principal_stress(eigenvalues(strain));
Tensor<2> t2 = some_function_to_generalize_T1_to_T2(t1);
return t2;
};
};