这是我根据您的问题发表的初步评论的形式更合理的副本。OP所解决问题的答案可以在该答案的底部找到。另外,请检查位于同一位置的重要说明。
您当前正在描述的Sipo是一种称为Active record的设计模式。与所有内容一样,即使是这一类也已在程序员中找到了位置,但出于一个简单的原因,即可伸缩性,它已被废弃,转而使用存储库和数据映射器模式。
简而言之,活动记录是一个对象,它:
- 代表您域中的对象(包括业务规则,知道如何处理该对象的某些操作,例如您是否可以更改用户名等等),
- 知道如何检索,更新,保存和删除实体。
您要解决当前设计中的几个问题,而设计的主要问题要解决的是第六点(我想是最后但并非最不重要的一点)。当您要为其设计构造函数的类时,甚至不知道构造函数应该做什么,则该类可能在做错事情。发生在您的情况下。
但是,通过将实体表示形式和CRUD逻辑分为两个(或更多)类,实际上可以很容易地确定设计。
这是您的设计现在的样子:
Employee
-包含有关雇员结构(其属性)和方法的信息,以及如何修改实体的方法(如果您决定采用可变方式),包含Employee
实体的CRUD逻辑,可以返回Employee
对象列表,Employee
在需要时接受对象更新员工,可以Employee
通过类似的方法返回单个getSingleById(id : string) : Employee
哇,全班人数很多。
这将是建议的解决方案:
Employee
-包含有关员工结构(其属性)以及如何修改实体的方法的信息(如果您决定采用可变方式)
EmployeeRepository
-包含Employee
实体的CRUD逻辑,可以返回Employee
对象列表,Employee
要更新员工时接受对象,可以Employee
通过类似方法返回单个getSingleById(id : string) : Employee
您听说过关注点分离吗?不,现在就可以。它是“单一责任原则”的较不严格的版本,它说一堂课实际上应该只承担一个责任,或者像鲍勃叔叔说的那样:
一个模块应该只有一个更改理由。
很显然,如果我能够将您的初始班级清晰地分为两个仍具有完善的接口的类,那么初始班级可能做得太多,而且确实如此。
存储库模式的优点是,它不仅充当了提供数据库之间中间层(可以是任何东西,文件,noSQL,SQL,面向对象的中间层)的抽象,而且甚至不需要是一个具体的类。在许多OO语言中,您可以将接口定义为实际接口interface
(如果使用C ++,则可以将接口定义为纯虚拟方法的类),然后可以实现多种实现。
这完全解除了存储库是否是您的实际实现的决策,而实际上只依赖于带有interface
关键字的结构,因此仅依赖于接口。而存储库正是这样,它是数据层抽象的一个奇特术语,即将数据映射到您的域,反之亦然。
将其分为(至少)两个类的另一个Employee
好处是,该类现在可以清楚地管理自己的数据并做得很好,因为它不需要处理其他困难的事情。
问题6:那么,构造函数应该在新创建的Employee
类中做什么?很简单。它应该接受参数,检查它们是否有效(例如,年龄不应该为负数或名称不为空),在数据无效时传递错误,并且是否通过验证将参数分配给私有变量实体的 现在,它无法与数据库通信,因为它根本不知道如何执行该操作。
问题4:根本无法回答,也不能一概而论,因为答案很大程度上取决于您的确切需求。
问题5:现在你已经分离臃肿类一分为二,你可以直接有多个更新方法Employee
类,如changeUsername
,markAsDeceased
,这将操纵的数据Employee
类仅在RAM中,然后你可以介绍的方法,例如registerDirty
从存储库类的工作单元模式,通过该模式,您可以让存储库知道该对象已更改属性,并且在调用该commit
方法后需要对其进行更新。
显然,对于更新而言,一个对象需要具有一个id并因此已经被保存,并且它是存储库的责任,可以检测到该ID并在不符合条件时引发错误。
问题3:如果决定采用工作单位模式,则create
方法现在为registerNew
。如果您不这样做,我可能会改称它save
。存储库的目标是提供域和数据层之间的抽象,因此,我建议您此方法(无论是registerNew
还是save
)都接受Employee
对象,并且由实现存储库接口的类决定,该类具有他们决定从实体中撤出。传递整个对象更好,因此您不需要具有许多可选参数。
问题2:这两种方法现在都将成为存储库接口的一部分,并且它们不违反单一职责原则。存储库的职责是为Employee
对象提供CRUD操作,即它所做的(除了读取和删除之外,CRUD转换为创建和更新)。显然,您可以通过使用EmployeeUpdateRepository
等来进一步拆分存储库,但这很少需要,并且单个实现通常可以包含所有CRUD操作。
问题1:您最终获得了一个简单的Employee
类,该类现在将(除其他属性外)具有ID。id是填充还是空(或null
)取决于对象是否已保存。尽管如此,id仍然是实体拥有的属性,并且实体的责任Employee
是照顾其属性,从而照顾其id。
实体是否具有ID通常并不重要,直到您尝试对其执行一些持久性逻辑。如对问题5的回答所述,存储库有责任检测到您不是要保存已保存的实体,还是要尝试更新没有ID的实体。
重要的提示
请注意,尽管关注点分离非常好,但实际上设计功能性存储库层是一项繁琐的工作,以我的经验,与主动记录方法相比,正确起来要困难得多。但是最终您将获得一个更加灵活和可扩展的设计,这可能是一件好事。
Employee
对象来提供抽象问题,问题4.和5.通常无法回答,取决于您的需要,并且如果将结构和CRUD操作分为两个类,那么很明显,Employee
不能获取数据的构造函数从数据库了,这样的答案6