我正在考虑OOP中针对以下问题的最佳实践:
我们有一个与外部API合作的程序。
API有一个类型为Element的对象,该对象基本上是一个几何元素。我们的应用程序是在几何模型上运行的验证应用程序应用程序获取这些元素的集合,并对它们执行一些几何测试。
我们用我们自己的称为"API"的类来包装这个API元素;ValidationElement";并将一些无法直接从API元素获得但我们的应用程序需要的附加信息保存到该包装器元素。
到目前为止还不错,但现在应用程序应该扩展并支持其他类型的模型(基本上我们可以说应用程序在不同的环境中运行)。特别是对于这种环境(它不适用于以前的情况),我们希望保存一个额外的参数,因为获得它会导致低性能。
实施它的最佳做法是什么?一方面,我希望避免添加与程序的特定(第一)部分无关的额外参数。另一方面,我不确定是否要使用继承并仅为这个小的附加属性拆分这个对象。
public class ValidationElement
{
public Element Element { get; set; }
public XYZ Location {get; set;}//The extra property
}
第一个简单的选择是,同一类将具有额外的属性和计算方法:
public class ValidationElement
{
public Element Element { get; set; }
public XYZ Location {get; set;}//The extra property
public string AdditionalProperty { get; set; }
public void HardProcessingCalcOfAdditionalProperty()
{
//hard processing
AdditionalProperty = result
}
}
我提到的第二个选项是继承
public class SecondTypeValidationElement : ValidationElement
{
public string AdditionalProperty { get; set; }
public void HardProcessingCalcOfAdditionalProperty()
{
//hard processing
AdditionalProperty = result
}
}
你认为这方面的最佳做法是什么?是否有其他方式或设计模式可以帮助我实现目标?
我希望避免添加与程序的特定(第一)部分无关的额外参数。
这似乎是一个迹象,表明这里应该避免继承。因为这种行为极有可能不适用于其他类别。
这是避免创建一些抽象的第二个原因:
基本上是一个几何元素的元素
因为:
- 所有派生元素都将具有这些附加属性
- 有许多文章显示了在几何图形中如何违反利斯科夫代换原理
所以我们更喜欢组合而不是继承。
因此,在我看来,如果我们将所有繁重的、紧密耦合的计算附加属性的逻辑转移到单独的类中,那将是非常好的:
public class ValidationElement
{
public string Element { get; set; }
public SomeExtra AdditionalProperty { get; set; }
}
public class SomeExtra
{
public string Location { get; set; }//The extra property
public string AdditionalProperty { get; set; }
public void HardProcessingCalcOfAdditionalProperty()
{
//hard processing
AdditionalProperty = string.Empty;
}
}
为什么我们创建了单独的类SomeExtra
并将逻辑放在这里:
- 如果我们想要编辑逻辑
HardProcessingCalcOfAdditionalProperty
,那么我们将只编辑一个类SomeExtra
。通过这样做,我们满足了SOLID原则中的单一责任原则 - 我们可以很容易地为SomeExtra创建一些抽象基类,然后在运行时我们可以决定应该注入什么具体实现。通过这样做,我们满足了SOLID原理的开闭原理
更新:
我真的很喜欢这个关于应该选择继承还是组合的答案:
我对以上内容的酸性测试是:
TypeB是否希望公开TypeA的完整接口(所有公共方法都不少于),以便在TypeA所在的位置使用TypeB预期?表示继承。
- 例如,塞斯纳双翼飞机将暴露飞机的完整界面,如果不是更多的话。因此,它适合从《飞机》中衍生出来
TypeB是否只希望TypeA暴露的行为的一部分?表示需要组合
- 例如,一只鸟可能只需要一架飞机的飞行行为。在这种情况下,将其提取为接口/类是有意义的/并使其成为这两个类的成员
更新:刚刚回到我的答案,如果没有具体提到Barbara Liskov的Liskov,它似乎是不完整的替换原则作为"我应该继承自"的测试这种类型?'
依赖反转原则:依赖倒置原则声明我们的类应该依赖于接口或抽象类,而不是具体的类和函数。
您的第一种编辑ValidationElement
类的方法通常是个坏主意,因为有几个环境可供项目运行。此外,使用这种方法维护和开发项目是不可扩展的,从长远来看将是一个令人头疼的问题。
开放-封闭原则:开放式-封闭原则要求类应开放以进行扩展,并关闭以进行修改。
我建议以下设计:
public interface IValidationElement
{
Element Element { get; set; }
XYZ Location {get; set;}//The extra property
// declare other base properties and methods
}
public class ValidationElement: IValidationElement
{
public Element Element { get; set; }
public XYZ Location {get; set;}//The extra property
// define other base properties and methods
}
public interface ISecondTypeValidationElement: IValidationElement
{
string AdditionalProperty { get; set; }
void HardProcessingCalcOfAdditionalProperty();
}
public class SecondTypeValidationElement: ISecondTypeValidationElement
{
public string AdditionalProperty { get; set; }
public void HardProcessingCalcOfAdditionalProperty()
{
//hard processing
AdditionalProperty = result
}
}
public interface IThirdEnvironmentValidationElement: IValidationElement
{
string ThirdProperty { get; set; }
void RequiredProcessing();
}
public class ThirdEnvironmentValidationElement: IThirdEnvironmentValidationElement
{
public string ThirdProperty { get; set; }
public void RequiredProcessing()
{
//related operations
}
}
我不会重复打开-关闭、DI或其他主体。已经讨论过了。我会看到这样的东西,或者甚至可以使用Extensions来设置值。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// old updated
public class Element
{
public Element(string msg) { Message = msg; }
public string Message;
}
public class XYZ { }
public class ABC { }
// new
public interface IDoesSomething
{
void SetResult();
}
// create 2 different wrappers
public class ValidationElementWrapper : IDoesSomething
{
public ValidationElementWrapper(Element el)
{
Element = el;
}
public Element Element { get; private set; }
public XYZ Location {get; set;}
public void SetResult()
{
Console.WriteLine("This is " + Element.Message);
// Do nothing
}
}
public class ValidationElementWrapper2 : IDoesSomething
{
public ValidationElementWrapper2(Element el)
{
Element = el;
}
public Element Element { get; private set; }
public XYZ Location {get; set;}
public string AdditionalProperty { get; set; }
public void SetResult()
{
AdditionalProperty = "Set additional property on wrapper 2";
Console.WriteLine("This is " + Element.Message + " and it has additional property - " + AdditionalProperty);
}
}
// run your program
public class Program
{
public static void Main()
{
var list = new List<IDoesSomething>();
list.Add(new ValidationElementWrapper(new Element("Element 1")));
list.Add(new ValidationElementWrapper2(new Element("Element 2")));
list.ForEach(item => item.SetResult());
}
}
输出
这是元素1
这是元素2,它有额外的属性-在包装2 上设置额外的属性
或者,您可以从更基本的类开始,然后继续扩展
public class ValidationElementWrapper : IDoesSomething
{
public ValidationElementWrapper(Element el)
{
Element = el;
}
public Element Element { get; private set; }
public XYZ Location {get; set;}
public virtual void SetResult() // <--- virtual
{
// Do nothing
Console.WriteLine("This is " + Element.Message);
}
}
public class ValidationElementWrapper2 : ValidationElementWrapper // <-- inheritnce
{
public ValidationElementWrapper2(Element el) : base(el)
{
}
public XYZ Location {get; set;}
public string AdditionalProperty { get; set; }
public override void SetResult() // <--- override
{
AdditionalProperty = "Set additional property on wrapper 2";
Console.WriteLine("This is " + Element.Message + " and it has additional property - " + AdditionalProperty);
}
}
结果将是相同的