同志们,告诉我在一个体面的社会中通常如何解决这样的问题:
例如,我们有这样一个类层次结构
class A_Base
{
int x;
int y;
public A_Base(int x, int y)
{
this.x = x;
this.y = y;
}
}
class A_First : A_Base
{
string name;
public A_First(int x, int y, string name):base(x,y)
{
this.name = name;
}
}
class A_Second : A_First
{
string surname;
public A_Second(int x, int y, string name, string surname):base(x,y,name)
{
this.surname = surname;
}
}
现在我们需要向 A_Base 添加一个属性(并相应地在构造函数中对其进行初始化)
class A_Base
{
int x;
int y;
int z;
public A_Base(int x, int y, int z)
{
this.x = x;
this.y = y;
this.z = z;
}
}
这意味着我们将不得不更改整个类层次结构中构造函数的定义,在那里添加一个新参数 z,尽管我们不更改这些构造函数本身——我们只更改对父类的调用。而且不知何故它不是很好。
class A_First : A_Base
{
string name;
public A_First(int x, int y, int z, string name):base(x,y,z)
{
this.name = name;
}
}
高级OOP的同志遇到这种情况一般是怎么操作的呢?有一个单独的类/结构将作为构造函数的参数吗?
class A_Init
{
int x;
int y;
int z;
}
class A_Base
{
int x;
int y;
int z;
public A_Base(A_Init init)
{
this.x = init.x;
this.y = init.y;
this.z = init.z;
}
}
还是有其他一些棘手的模式?
这是继承的缺点之一——如果父类发生变化,那么这些变化很可能会影响所有子类。在这里什么也做不了。这种缺点在长期的、大规模的项目中会表现得尤为明显。
为了避免这样的事情,可以考虑用组合代替继承,或者熟悉 SOLID 设计,或者看看其他范例,例如“面向组件的编程”。
在后者中,每个类都是由组件构建的,没有分别使用继承,问题消失了,出错的概率也小了很多,但是当组件很多时,导航起来会很困难。
在 C# 语言中,没有办法。这是脆性基类问题的特例。
您必须明白,更改基类(某处有或可能有派生类)始终是重大更改,需要仔细审查整个代码。
这就是为什么
double,现在它以弧度为单位的角度以及类型的数量double[Obsolete]是的,修改基类违反了SOLID 的开放/封闭原则。
tl;博士
在这种特殊情况下,我会注意更改规范的原因。新参数从哪里来
z,为什么没有立即提供?这个问题的答案将有助于正确设计层次结构。例如(选项 #1),主题专家无法描述所有可能的场景。这意味着当您处理程序时,属性会出现和改变,从业务角度来看这是完全正常的。那么是的,类型结构
A_Init将是一个合适的解决方案。另一方面,如果此结构存储参数
x,y,的默认值z,那么立即在层次结构中创建一个空构造函数或仅包含最必要参数的构造函数不是更好吗,其列表肯定会不改变©?第二种方法更正确,除其他外,因为它有助于在层次结构的所有级别解决相同的问题。
A_Init不保证您不必A_Init2为任何继承人开始。因为:尽可能使用默认值。仅通过构造函数传递
readonly字段初始值设定项。选项编号 2:正在进行系统的初步设计,正在编写原型。对于原型来说,经常更改代码并没有那么可怕。实际上,它的开发是为了识别瓶颈。那么答案就是:没有什么可怕的事情发生,应该如此。
第三种选择:继承人不是真正的继承人,而是装饰者。然后实现看起来不同:
如何理解在这种情况下利害攸关的是什么?通常这样它
A_Base是一个几乎什么都不能做的抽象类,但是它的继承者做主要的工作。第四个选项是第一个选项的扩展。如果存在默认值,但获取它们的算法令人困惑,则获取/计算它们的逻辑可以在一个单独的类中取出,Eric Evans 将其称为regulation。然后层次结构中的所有类都会在构造函数中接受一个或多个规则,并根据业务逻辑规则进行初始化。
与规定的区别
A_Init不在于句法,而在于本质。如果A_Init它不包含业务逻辑,而只是返回常量,那么这很可能不是规定。