C# 在DDD中创建核心模型时如何分离/解耦子实例的创建
课程可以有多种活动,即培训、考试、项目、书籍、文章和任务。 以下是要求:C# 在DDD中创建核心模型时如何分离/解耦子实例的创建,c#,oop,domain-driven-design,C#,Oop,Domain Driven Design,课程可以有多种活动,即培训、考试、项目、书籍、文章和任务。 以下是要求: 允许老师安排课程 允许教师在所述课程中安排不同的活动 在指定日期范围内向学生显示所选课程的活动列表 以上要求使我创建了两个聚合 课程总门 活动聚合 为什么?? 课程可以在没有任何活动的情况下创建,但只能在草稿状态下创建。可以为不同的学生安排课程。 当然,可以创建独立于课程的活动,然后再链接到课程。 只能为给定的学生获取日期范围内的活动 protected abstract class Activity {
- 允许老师安排课程
- 允许教师在所述课程中安排不同的活动
- 在指定日期范围内向学生显示所选课程的活动列表
- 课程总门
- 活动聚合
protected abstract class Activity
{
public Guid Id {get; private set;}
}
protected class Training : Activity
{
..... Addiontal properties
}
protected class Exam : Activity
{
....Addiontal properties and behavior.
public bool AllowGrading => true;
}
.... Other childern of activity..hence more classes.
问题:
- 这是继承的正确方法吗
- 因为我将构造函数标记为受保护的,所以客户端代码不会使用新的运算符,也不会直接了解子对象。我正在努力弄清楚客户机应该如何创建活动实例。例如:
如何正确地解决问题,实现它,或者哪种模式更适合这里?这是使用C或Java等语言时经常出现的问题之一。这是一个实现问题,而不是建模问题 问题是你确实有这些概念:
考试
,培训
等等,这些概念都是具体的。另一方面,您可以为它们派生一个通用概念:活动
这里是我们在考虑实现之前需要问的几个问题。
- 它需要如何处理这些概念李>
- 它是如何与他们合作的李>
- 系统中有多少部分对具体概念
、考试
等感兴趣,又有多少部分对培训
的共同概念感兴趣活动
- 您是否希望添加更多将成为
活动的概念?这将影响你如何发展你的系统
Activity
的概念,并且不会添加更多的活动。在这种情况下,我们可以忽略活动,只使用具体的概念。这意味着创建它们没有问题
假设您的系统将使用活动的概念,您需要添加更多类型的活动
这并不会破坏这样一个事实,即您的系统将知道不同的具体活动类型。它将创建它们,使用它们等等。即使您的系统正在使用活动的概念,它可能仍然需要知道活动的具体类型,以便可以使用它做一些事情
这种逻辑表明,当我们使用像Java的C#这样的OOP语言时,我们的思维方式存在问题。我们被训练成开发者。通常人们说演员阵容不好。您应该以某种方式定义基类或接口,让接口实现者的子类定义行为,而系统的其他部分不应该知道具体的类型
这对于系统的某些部分和未来的概念来说是正确的。以序列化程序为例。您可以使用方法Serialize
定义接口ISerializer
。使用序列化程序的系统可以使用该接口,而不必知道具体的类型,因为实现ISerializer
接口的每个类都将添加相同接口的不同实现
不是每个问题都是这样的。有时,您的系统需要知道它处理的是什么。在这里,我想我们可以从JavaScript这样的语言中学到一些东西。在这里,您拥有的是一个非特定的对象,用户可以将属性附加到该对象。对象就是它的属性定义的对象
这个概念很有趣:“如果它走路像鸭子,嘎嘎叫像鸭子,那么它一定是鸭子。”
如果您的系统需要使用检查
,它应该使用它,而不是使用活动
。如果它有一个活动
,它应该能够找出它,它确实是一个考试
,因为这是它需要的
现在我们生活在一个强类型的世界,它有它的好部分。我喜欢强打字和它给你的东西,但也有一些问题更难处理
您可以使用继承类来实现这一点。您还可以使用接口而不是类来捕获不同的概念。然而,您的系统需要进行一些转换,以确定正在使用的具体类型。如果我们明确地捕捉到我们有不同类型的活动
这一事实,我们可以让生活变得更轻松
下面是一个例子:
public enum ActivityType { Exam, Trainig, Project, Task }
public class Activity {
public Guid ID { get; private set; }
public abstract ActivityType Type { get; }
// other stuff
}
public class Exam : Activity {
public override ActivityType Type {
get { return ActivityType.Exam; }
}
// other stuff
}
public class SomeClass {
public void SomeAction(Activity activity) {
if(activity.Type == ActivityType.Exam) {
var examActivity = (Exam)activity;
// do something with examActivity
}
}
}
如果创建活动有一些与之相关的逻辑,您可以使用工厂
通过使用它们的具体类型来创建它们
public class ExamFactory {
public Exam CreateSummerExam(string name, //other stuff) {
// Enfore constraints
return new Exam(new Guid(), name,....);
}
}
或在混凝土类型中添加工厂
:
public class Exam : Activity {
public static Exam CreateSummerExam() {
// Enfore constraints
return new Exam();
}
private Exam() { }
}
如果创建这些对象并不复杂,也可以使用公共构造函数
如果您真的想隐藏类以允许自己自由实现,请使用接口:
// **Domain.dll**
public enum ActivityType { Exam, Training }
public interface IActivity {
ActivityType Type { get; }
}
public interface IExam : IActivity { }
internal class Exam : IExam { }
public class ActivityFactory {
public IExam CreateExam() { return new Exam(); }
public ITraining CreateTraining() { return new Training(); }
// other concrete activities
}
这样就不允许clien代码访问类。您可以授予他们访问公共接口的权限,并将其他特定于实现的方法保留在Domain.dll的内部。这些概念的客户机仍然可以使用强制转换来使用他们需要的适当类型,但这次他们将使用接口
这是一个很好的例子。马丁·福勒在信中说:
使用OO程序,您试图避免
// **Domain.dll**
public enum ActivityType { Exam, Training }
public interface IActivity {
ActivityType Type { get; }
}
public interface IExam : IActivity { }
internal class Exam : IExam { }
public class ActivityFactory {
public IExam CreateExam() { return new Exam(); }
public ITraining CreateTraining() { return new Training(); }
// other concrete activities
}
if(activity.type==exam){ ((Exam)IActivity).DoSomething();}