软件设计之用例图和类图

发表于:2020-9-29 09:37

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:冲冲冲冲冲    来源:博客园

  需求分析
  披萨店订单系统针对连锁企业设计,涉及不同地区、不同风味的多个门店。
  1.不同门店采用的原材料可以自定义。
  2.门店将来可能会增加披萨的其他操作。
  3.不同门店可以增加特色披萨。
  4.点单时客户可以高度自定义披萨、饮料。
  5.因为是连锁店,菜单价格需要统一控制。
  6.门店将来可能会增加除了披萨、饮料外的其他类型餐点。
  7.为满足以上需求,所设计的系统具有弹性且必须充分符合OO原则,便于后续开发。
  用例图
  基于以上需求分析,可以初步得到如下的用例图:
  从客户和管理员两个actor出发:
  ·客户点单的用例较为简单,就是首先选择披萨店,然后进行点单(包括点披萨和点饮料两部分,由于需求6,后续可能会增加新类型的商品)
  ·管理员管理披萨店,可以新增连锁的披萨店,也可以修改现有披萨店的信息。管理的主要内容目前为对菜单的修改,包括新增菜单,新增可点的商品(若是新建连锁店,则前两步必须进行),以及修改现有的菜单。
  类图
  为满足以上需求,所设计的系统具有弹性且必须充分符合OO原则,便于后续开发。所以我们运用了很多经典的设计模式,下面将基于这些设计模式完成类图的设计。
  ·工厂模式。为满足需求2,采用工厂模式设计门店。
  根据依赖倒置原则,倒置设计思路,不从“顶端”的披萨店开始设计,而从披萨开始。首先抽象出一个Pizza类,再回头思考如何设计PizzaStore类,这样PizzaStore类就会依赖抽象的Pizza类,而不需要理会具体的Pizza类,从而使得具体不同种类的披萨和抽象的PizzaStore类都依赖于这个抽象的Pizza类,从而使得设计符合依赖倒置原则。而PizzaStore则通过工厂方法创建具体Pizza。工厂方法模式的类图:
  为了满足需求1,再创建一个原料工厂,负责创建Pizza所需的面饼、酱料、芝士等原料,供制作Pizza时使用。Pizza的代码利用相关的工厂生产原料,所生产的原料依赖所使用的工厂,Pizza类根本不关心这些原料,从而实现Pizza和具体原料的完全解耦。
  因此整个工厂实际上是抽象工厂模式,允许披萨店使用抽象接口获得一组相关产品(原料),从而使披萨店和原料解耦。
  通过工厂模式,我们可以很容易地创建新的原料工厂和披萨店,且符合开闭原则和接口原则,只需要直接增加新的类,实现PizzaStore和PizzaIngredientFactory中的抽象方法即可,使得整个系统非常具有弹性。
  ·装饰者模式。为满足需求3和需求4,可以用装饰者模式负责创建自定义Pizza。由于需要自定义Pizza,涉及到属性和价格的变化,为了满足开闭原则,使用装饰者模式是最佳选择。
  即每个装饰的组件和基本的被装饰的组件,均继承自Pizza抽象类,并重写其cost()和prepare()等方法,每个装饰组件均有一个指针指向被装饰者,从而使得这些方法可以先委托给被装饰者,然后再调用自己的方法,从而实现动态地将责任附加到对象上,可以更弹性地扩展功能。
  ·单件模式。为满足需求5,需要使用单件模式,创建全局唯一的价目表对象。
  因为全国连锁需要保证价格统一,所以价目表只能有一个实例,否则会导致许多问题的产生(程序行为异常、资源使用过量)。虽然全局变量也可以实现这个功能,但是如果将对象赋值给一个全局变量,那么必须在程序一开始就创建好对象,如果这个对象非常耗费资源,而程序在这次的执行过程中又一直没用到它,就会很浪费。而单件模式只有需要用到的时候才会创建对象。
  以所有饮料及其配料为例,所有的cost()方法均调用Menu类中的getXXPrice()方法获取价格。通过让单件模式让Menu类的对象在运行时只有一个,保证价格的统一性。
  ·命令模式。考虑订单的处理流程:首先顾客将订单交给前台,然后前台转交给对应的披萨店,然后披萨店按照订单开始准备披萨,最后返回给顾客。在整个流程中,应该将发出请求的客户方和接收与执行请求的披萨店解耦,这时可以采用命令模式。
  本系统中,我们这样运用了命令模式:首先客户创建订单对象Order;然后调用setOrder()方法将订单对象存储在调用者OrderHandler对象中;然后客户要求调用者执行命令,即调用OrderHandler中的handleOrder()方法,该方法中又会调用Order的excute()方法,返回Pizza。而具体如何返回以及返回哪个店的Pizza,则通过Order的具体实现类重写excute()方法实现。
  ·适配器模式。为满足需求6,且符合开闭原则,采用适配器模式,将所有的餐点通过Adapter转换成Pizza类型。
  图中可以看到OrderHandler中有一个printReceipt(List)方法,该方法通过传入一个Pizza类型的列表打印菜单。若不采用适配器模式,则每次增加新增菜品时,均需要重新修改此处的代码,增加对应新类型参数的方法,违背OO原则。
  由于饮料没有prepare()、bake()、cut()、box()方法,因此不用关心。而description()和cost()方法是饮料类有的,因此可以直接调用饮料类的这两个方法,从而将饮料类转换成了Pizza类。使用了适配器模式后,若后续增加新类型的餐点,只需要增加新的Adapter类将其与Pizza对接,而无需修改OrderHandler中的代码,符合开闭原则。

  本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号