C# 面向对象编程:数据和行为的分离

C# 面向对象编程:数据和行为的分离,c#,oop,design-patterns,C#,Oop,Design Patterns,最近我们讨论了课堂上的数据和行为分离。数据和行为分离的概念是通过将域模型及其行为放在不同的类中来实现的。 然而,我不相信这种方法的所谓好处。尽管它可能是由一位“伟人”创造的(我想这是马丁·福勒,尽管我不确定)。我在这里给出一个简单的例子。假设我有一个Person类,其中包含Person及其方法(行为)的数据 班级人员 { 字符串名; 日期时间出生日期; //建造师 Person(字符串名称、日期时间、出生日期) { this.Name=Name; this.BirthDate=生日; } int

最近我们讨论了课堂上的数据和行为分离。数据和行为分离的概念是通过将域模型及其行为放在不同的类中来实现的。
然而,我不相信这种方法的所谓好处。尽管它可能是由一位“伟人”创造的(我想这是马丁·福勒,尽管我不确定)。我在这里给出一个简单的例子。假设我有一个Person类,其中包含Person及其方法(行为)的数据

班级人员
{
字符串名;
日期时间出生日期;
//建造师
Person(字符串名称、日期时间、出生日期)
{
this.Name=Name;
this.BirthDate=生日;
}
int GetAge()
{
今天返回-生日;//仅供说明
}
}
现在,将行为和数据分离到单独的类中

班级人员
{
字符串名;
日期时间出生日期;
//建造师
Person(字符串名称、日期时间、出生日期)
{
this.Name=Name;
this.BirthDate=生日;
}
}
班级人事服务
{
人-人-客体;
//建造师
PersonService(字符串名称、日期时间、生日)
{
this.personObject=新人(姓名、出生日期);
}
//重载构造函数
PersonService(personObject)
{
this.personObject=personObject;
}
int GetAge()
{
返回personObject.Today-personObject.BirthDate;//仅供说明
}
}
这应该是有益的,可以提高灵活性并提供松耦合。我不知道该怎么做。据我所知,这会带来额外的编码和性能损失,每次我们都必须初始化两个类对象。我发现扩展这段代码会有更多问题。考虑当我们在上面的案例中引入继承时会发生什么。我们必须继承这两个类

class员工:个人
{
双薪;
员工(字符串名称、日期时间、生日、双倍工资):基数(姓名、生日)
{
这个。薪水=薪水;
}
}
类别EmployeeService:PersonService
{
员工对象;
//建造师
EmployeeService(字符串名称、日期时间、生日、双薪)
{
this.employeeObject=新员工(姓名、生日、工资);
}
//重载构造函数
EmployeeService(员工employeeObject)
{
this.employeeObject=employeeObject;
}
}
请注意,即使我们在一个单独的类中分离出行为,我们仍然需要数据类的对象,以便行为类方法可以处理。因此,我们的行为类最终包含数据和行为,尽管数据是以模型对象的形式存在的。
您可能会说,您可以在混合中添加一些接口,这样我们就可以拥有IPersonService和IEmployeeService。但我认为为每个类引入接口并从接口中插入似乎并不合适。


那么,你能告诉我,在上述情况下,通过分离数据和行为,我取得了什么成就,而在同一个类中,我无法实现这些成就?

事实上,Martin Fowler说,在域模型中,数据和行为应该结合起来。看看。

有趣的是,OOP通常被描述为数据和行为的结合

你在这里展示的是我认为的一种反模式:“贫血域模型”。它确实遭受了你提到的所有问题,并且应该避免。


应用程序的不同级别可能会有更多的程序性倾向,这有助于您所展示的服务模型,但这通常只在系统的最边缘。即便如此,这将由传统的对象设计(数据+行为)在内部实现。通常,这只是头痛

对一个人(任何人)有兴趣的年龄。因此,它应该是Person对象的一部分

hasExperienceWithThe40mmRocketLauncher()不是person的固有特性,而是可以扩展或聚合person对象的接口军事服务的固有特性。因此,它不应该是Person对象的一部分

一般来说,我们的目标是避免向基本对象(“Person”)添加方法,因为这是最简单的解决方法,因为您将异常引入到正常的Person行为中

基本上,如果您看到自己将“hasServedInMilitary”之类的东西添加到基本对象中,您就有麻烦了。接下来,您将执行大量语句,例如if(p.hasservedinmarity())blabla。这在逻辑上与一直执行instanceOf()检查是一样的,表明Person和“看过兵役的人”实际上是两个不同的东西,应该以某种方式断开连接


退一步说,OOP是关于减少if和switch语句的数量,而是让各种对象根据其抽象方法/接口的特定实现来处理事情。将数据和行为分离会促进这一点,但没有理由将其推向极端,并将所有数据与所有行为分离。

我同意,您实现的分离很麻烦。但还有其他选择。有方法getAge(person p)的ageCalculator对象呢?或person.getAge(IAgeCalculator calc)。或者更好的计算方法(IAgeble a)

分离这些关注点会带来一些好处。假设您打算让您的实现返回数年,那么如果一个人/婴儿只有3个月大怎么办?你是否返回0。25? 抛出异常?如果我想要狗的年龄呢?几十年还是几个小时?如果我想要某个日期的年龄呢?如果这个人死了怎么办?如果我想使用火星轨道一年呢?还是希伯来卷发

所有这些都不会影响使用person接口但不使用生日或年龄的类。通过装饰