pc网站转换成微网站,购物网站建设计划书,湖南省网站,专业做ea的网站域驱动设计#xff08;DDD#xff09;是一种软件开发方法#xff0c;其中#xff0c;通过将实现与核心业务概念的不断发展的模型相连接#xff0c;可以解决问题的复杂性。 该术语是由Eric Evans创造的#xff0c;并且有一个DDD专用站点可以促进其使用。 根据其定义#… 域驱动设计DDD是一种软件开发方法其中通过将实现与核心业务概念的不断发展的模型相连接可以解决问题的复杂性。 该术语是由Eric Evans创造的并且有一个DDD专用站点可以促进其使用。 根据其定义 “域驱动设计术语表” DDD是一种软件开发方法它建议 对于大多数软件项目主要重点应该放在域和域逻辑上 复杂的领域设计应基于模型。 DDD促进了技术专家和领域专家之间的创造性合作以迭代方式切入问题的概念核心。 请注意没有该领域专家的帮助技术专家可能无法完全理解领域的复杂性而领域专家在没有技术专家帮助的情况下就无法实际应用其知识。 在许多情况下领域模型对象封装了内部状态本质上就是元素的历史即对象以有状态方式运行。 在那种情况下对象保持其私有状态这最终会影响其行为。 为了表示对象的状态以及以干净的方式处理其状态转换可以使用状态设计模式 。 简而言之 状态模式可以解决如何使行为取决于状态的问题。 显然DDD与状态设计模式紧密相关。 我是DDD的新手所以我将让我们最好的JCG合作伙伴之一 Tomasz Nurkiewicz 通过使用State Design Pattern的示例向您介绍DDD 。 注意对原始帖子进行了少量编辑以提高可读性 许多企业应用程序中的某些领域对象都包含状态的概念。 国家有两个主要特征 域对象的行为其对业务方法的响应方式取决于其状态 业务方法可能会更改对象的状态从而迫使对象在调用特定方法后的行为有所不同。 如果您无法想象域对象状态的任何真实示例请考虑租赁公司中的Car实体。 小汽车在保留相同对象的同时还有一个附加标志称为状态这对于公司至关重要。 状态标志可以具有三个值 可用的 出租 失踪 显然目前无法租用处于RENTED或MISSING状态的Car并且rent方法应该失败。 但是当汽车退回并且其状态为AVAILABLE时除了记住已租车的客户外在Car实例上调用rent应该应该将汽车状态更改为RENTED。 状态标志可能是数据库中的单个字符或整数是对象状态的一个示例因为它影响业务方法反之亦然业务方法可以更改它。 现在想一会儿您将如何实现这种方案我相信您已经在工作中见过很多次了。 您有许多业务方法具体取决于当前状态也可能取决于多个状态。 如果您喜欢面向对象的编程则可能会立即考虑继承并创建扩展Car的AvailableCarRentedCar和MissingCar类。 它看起来不错但是非常不切实际特别是当Car是一个持久对象时。 实际上这种方法设计得不好改变的不是整个对象而是内部状态的一部分–我们不是在替换对象而只是在更改它。 也许您考虑过在每个方法中根据状态执行不同任务的if-else-if-else级联。 相信我不要去那里那是通往代码维护地狱的道路。 取而代之的是我们将使用继承和多态性但是要采用一种更为巧妙的方式使用State GoF模式 。 例如我选择了一个名为Reservation的实体该实体可以具有以下状态之一 生命周期流程很简单创建保留时它具有NEW状态状态。 然后一些授权人员可以接受预订例如导致临时预订座位并向用户发送一封电子邮件要求他为预订付款。 然后当用户执行汇款时将进行入帐打印票证并将第二封电子邮件发送给客户。 当然您知道某些动作的副作用取决于保留当前状态。 例如您可以随时取消预订但是根据预订状态这可能会导致退款和取消预订或者仅向用户发送电子邮件。 此外某些操作在特定状态下用户将钱转至已取消的预订该怎么办毫无意义或应被忽略。 现在想象一下如果必须为每个状态和每个方法使用if-else构造那么编写上面状态机图上公开的每个业务方法将有多么困难。 为了解决此问题我将不解释原始的GoF State设计模式。 相反我将使用Java枚举功能介绍这种模式的一些变化。 代替为状态抽象创建抽象类/接口并为每个状态编写实现我仅创建了一个包含所有可用状态/状态的枚举 public enum ReservationStatus {NEW,ACCEPTED,PAID,CANCELLED;
} 我还根据该状态为所有业务方法创建了一个接口。 将此接口视为所有状态的抽象基础但是我们将以稍微不同的方式使用它 public interface ReservationStatusOperations {ReservationStatus accept(Reservation reservation);ReservationStatus charge(Reservation reservation);ReservationStatus cancel(Reservation reservation);
} 最后是Reservation域对象它恰好同时是一个JPA实体省略了getters / setter或者也许我们可以只使用Groovy而忘记它们了 public class Reservation {private int id;private String name;private Calendar date;private BigDecimal price;private ReservationStatus status ReservationStatus.NEW;//getters/setters} 如果Reservation是一个持久域对象则其状态ReservationStatus显然也应该是持久的。 这种观察将我们带到了使用枚举而不是抽象类的第一个重大优势JPA / Hibernate可以使用枚举的名称或序数值默认情况下轻松地序列化Java枚举并将其保留在数据库中。 在原始GoF模式中我们宁愿将ReservationStatusOperations直接放在域对象中并在状态更改时切换实现。 我建议使用枚举仅更改枚举值。 使用枚举的另一个优点以框架为中心更不重要是将所有可能的状态都列在一个位置。 您无需搜寻源代码即可搜索基状态类的所有实现所有内容都可以在一个逗号分隔的列表中看到。 好吧深吸一口气现在我将解释所有这些部分如何协同工作以及到底为什么ReservationStatusOperations中的业务操作返回ReservationStatus。 首先您必须回顾实际的枚举是什么。 它们不仅仅是像C / C 中的单个名称空间中的常量的集合。 在Java中枚举是一组封闭的类集它们从一个通用的基类例如ReservationStatus继承而该基类又从Enum继承。 因此在使用枚举时我们可能会利用多态和继承 public enum ReservationStatus implements ReservationStatusOperations {NEW {public ReservationStatus accept(Reservation reservation) {//..}public ReservationStatus charge(Reservation reservation) {//..}public ReservationStatus cancel(Reservation reservation) {//..}
},ACCEPTED {public ReservationStatus accept(Reservation reservation) {//..}public ReservationStatus charge(Reservation reservation) {//..}public ReservationStatus cancel(Reservation reservation) {//..}
},PAID {/*...*/},CANCELLED {/*...*/};} 尽管试图以这种方式编写ReservationStatusOperations很诱人但对于长期开发而言这是一个坏主意。 不仅枚举源代码会很长已实现方法的总数等于状态数量乘以业务方法的数量而且设计不好单个类中所有状态的业务逻辑。 同样对于在过去两周内未通过SCJP考试的任何人来说实现与该语法的其余部分一起使用的接口的枚举可能都是相反的。 相反我们将提供一个简单的间接级别因为“ 计算机科学中的任何问题都可以通过另一层间接解决 ”。 public enum ReservationStatus implements ReservationStatusOperations {NEW(new NewRso()),ACCEPTED(new AcceptedRso()),PAID(new PaidRso()),CANCELLED(new CancelledRso());private final ReservationStatusOperations operations;ReservationStatus(ReservationStatusOperations operations) {this.operations operations;}Overridepublic ReservationStatus accept(Reservation reservation) {return operations.accept(reservation);}Overridepublic ReservationStatus charge(Reservation reservation) {return operations.charge(reservation);}Overridepublic ReservationStatus cancel(Reservation reservation) {return operations.cancel(reservation);}} 这是我们ReservationStatus枚举的最终源代码无需实现ReservationStatusOperations。 简而言之每个枚举值都有其自己的ReservationStatusOperations简称Rso的不同实现。 此实现作为构造函数参数传递并分配给名为operation的最终字段。 现在每当在枚举上调用业务方法时它将被委派给该枚举专用的ReservationStatusOperations实现 ReservationStatus.NEW.accept(reservation); // will call NewRso.accept()
ReservationStatus.ACCEPTED.accept(reservation); // will call AcceptedRso.accept() 最后一个难题是Reservation域对象包括业务方法 public void accept() {setStatus(status.accept(this));
}public void charge() {setStatus(status.charge(this));
}public void cancel() {setStatus(status.cancel(this));
}public void setStatus(ReservationStatus status) {if (status ! null status ! this.status) {log.debug(Reservation# id : changing status from this.status to status);this.status status;} 这里会发生什么 在保留域对象实例上调用任何业务方法时将在ReservationStatus枚举值上调用相应的方法。 根据当前状态将调用不同的方法具有不同的ReservationStatusOperations实现。 但是没有切换用例或if-else构造只有纯多态性。 例如如果您在状态字段指向ReservationStatus.ACCEPTEDAcceptedRso.charge的情况下调用charge则向预订的客户收取费用并且预订状态更改为PAID。 但是如果我们在同一实例上再次调用charge会发生什么呢 status字段现在指向ReservationStatus.PAID因此将执行PaidRso.charge这将引发业务异常对已付费的预订收取费用无效。 在没有条件代码的情况下我们使用对象本身包含的业务方法实现了状态感知域对象。 我还没有提到的一件事是如何从业务方法更改状态。 这是与原始GoF模式的第二个区别。 我没有将StateContext实例传递给每个可用于更改状态的状态感知操作例如accept或charge而是仅从业务方法返回新状态。 如果状态不为null并且与前一个状态不同setStatus方法则保留将转换为给定状态。 让我们看一下它如何在AcceptedRso对象上工作当Reservation处于ReservationStatus.ACCEPTED状态时将执行其方法 public class AcceptedRso implements ReservationStatusOperations {Overridepublic ReservationStatus accept(Reservation reservation) {throw new UnsupportedStatusTransitionException(accept, ReservationStatus.ACCEPTED);}Overridepublic ReservationStatus charge(Reservation reservation) {//charge clients credit card//send e-mail//print ticketreturn ReservationStatus.PAID;}Overridepublic ReservationStatus cancel(Reservation reservation) {//send cancellation e-mailreturn ReservationStatus.CANCELLED;}} 仅需阅读上面的课程即可很容易地了解处于“已接受”状态的预订行为第二次尝试接受已接受预订时将引发异常收费将向客户的信用卡收取费用向其打印一张机票并发送电子邮件等。此外收费会返回PAID状态这将导致预订转移到该状态。 这意味着另一个对charge的调用将由不同的ReservationStatusOperations实现PaidRso处理没有条件代码。 这将全部与国家模式有关。 如果您对这种设计模式不满意请与使用条件代码的经典方法进行比较并比较工作量和出错率。 还要考虑一会儿添加新的状态或与状态有关的操作时需要什么以及阅读这样的代码有多容易。 我没有显示所有ReservationStatusOperations实现但是如果您想在基于Spring或EJB的Java EE应用程序中引入这种方法那么您可能会发现其中的一个大谎言。 我评论了每种业务方法中应发生的情况但未提供实际的实现。 我没有因为我遇到了一个大问题一个Reservation实例是手工创建的使用新的或由诸如Hibernate之类的持久性框架创建的。 它使用静态创建的枚举该枚举可手动创建ReservationStatusOperations实现。 无法将任何依赖项DAO和服务注入此类因为它们的生命周期是在Spring或EJB容器范围之外进行控制的。 实际上有一个使用Spring和AspectJ的简单而强大的解决方案。 但是请耐心等待我将在下一篇文章中详细解释它为我们的应用程序添加一些域驱动的风格。 而已。 我们的JCG合作伙伴 Tomasz Nurkiewicz撰写了一篇非常有趣的文章介绍如何在DDD方法中利用状态模式 。 我当然很期待本教程的下一部分该教程将在几天后在JavaCodeGeeks上托管。 更新下一部分是使用Spring和AspectJ的域驱动设计 。 相关文章 Spring和AspectJ的领域驱动设计 零XML的Spring配置 正确记录应用程序的10个技巧 每个程序员都应该知道的事情 依赖注入–手动方式 翻译自: https://www.javacodegeeks.com/2011/02/state-pattern-domain-driven-design.html