软件设计与体系结构大作业-设计模式


代码地址: https://github.com/anlowee/pizza-system
https://github.com/anlowee/pizza-system-minecraft
设计模式:

  1. 工厂模式。为满足需求2,采用工厂模式设计门店。
    根据依赖倒置原则,倒置设计思路,不从“顶端”的披萨店开始设计,而从披萨开始。首先抽象出一个Pizza类,再回头思考如何设计PizzaStore类,这样PizzaStore类就会依赖抽象的Pizza类,而不需要理会具体的Pizza类,从而使得具体不同种类的披萨和抽象的PizzaStore类都依赖于这个抽象的Pizza类,从而使得设计符合依赖倒置原则。而PizzaStore则通过工厂方法创建具体Pizza。工厂方法模式的类图:

    为了满足需求1,再创建一个原料工厂,负责创建Pizza所需的面饼、酱料、芝士等原料,供制作Pizza时使用。Pizza的代码利用相关的工厂生产原料,所生产的原料依赖所使用的工厂,Pizza类根本不关心这些原料,从而实现Pizza和具体原料的完全解耦。

    因此整个工厂实际上是抽象工厂模式,允许披萨店使用抽象接口获得一组相关产品(原料),从而使披萨店和原料解耦。

    通过工厂模式,我们可以很容易地创建新的原料工厂和披萨店,且符合开闭原则和接口原则,只需要直接增加新的类,实现PizzaStore和PizzaIngredientFactory中的抽象方法即可,使得整个系统非常具有弹性。
  2. 装饰者模式。为满足需求3和需求4,可以用装饰者模式负责创建自定义Pizza。由于需要自定义Pizza,涉及到属性和价格的变化,为了满足开闭原则,使用装饰者模式是最佳选择。

    即每个装饰的组件和基本的被装饰的组件,均继承自Pizza抽象类,并重写其cost()和prepare()等方法,每个装饰组件均有一个指针指向被装饰者,从而使得这些方法可以先委托给被装饰者,然后再调用自己的方法,从而实现动态地将责任附加到对象上,可以更弹性地扩展功能。
  3. 单件模式。为满足需求5,需要使用单件模式,创建全局唯一的价目表对象。
    因为全国连锁需要保证价格统一,所以价目表只能有一个实例,否则会导致许多问题的产生(程序行为异常、资源使用过量)。虽然全局变量也可以实现这个功能,但是如果将对象赋值给一个全局变量,那么必须在程序一开始就创建好对象,如果这个对象非常耗费资源,而程序在这次的执行过程中又一直没用到它,就会很浪费。而单件模式只有需要用到的时候才会创建对象。
    以所有饮料及其配料为例,所有的cost()方法均调用Menu类中的getXXPrice()方法获取价格。通过让单件模式让Menu类的对象在运行时只有一个,保证价格的统一性。
  4. 命令模式。考虑订单的处理流程:首先顾客将订单交给前台,然后前台转交给对应的披萨店,然后披萨店按照订单开始准备披萨,最后返回给顾客。在整个流程中,应该将发出请求的客户方和接收与执行请求的披萨店解耦,这时可以采用命令模式。

    本系统中,我们这样运用了命令模式:首先客户创建订单对象Order;然后调用setOrder()方法将订单对象存储在调用者OrderHandler对象中;然后客户要求调用者执行命令,即调用OrderHandler中的handleOrder()方法,该方法中又会调用Order的excute()方法,返回Pizza。而具体如何返回以及返回哪个店的Pizza,则通过Order的具体实现类重写excute()方法实现。
  5. 适配器模式。为满足需求6,且符合开闭原则,采用适配器模式,将所有的餐点通过Adapter转换成Pizza类型。
    图9中可以看到OrderHandler中有一个printReceipt(List )方法,该方法通过传入一个Pizza类型的列表打印菜单。若不采用适配器模式,则每次增加新增菜品时,均需要重新修改此处的代码,增加对应新类型参数的方法,违背OO原则。

    由于饮料没有prepare()、bake()、cut()、box()方法,因此不用关心。而description()和cost()方法是饮料类有的,因此可以直接调用饮料类的这两个方法,从而将饮料类转换成了Pizza类。使用了适配器模式后,若后续增加新类型的餐点,只需要增加新的Adapter类将其与Pizza对接,而无需修改OrderHandler中的代码,符合开闭原则。
  6. RESTFUL API设计模式 以及 前端控制器模式。在Controller层,采用restful的api设计。
  7. 观察者模式。不同于传统前端,我们小组的前端采用minecraft plugin的模式。为了与游戏交互,需要监听游戏中产生的各种事件,而这就需要我们使用观察者模式,通过注册用于监听的Listener类,即可实现监听不同的事件,再去具体实现对应的业务逻辑。

    例如OrderListener类中,通过监听玩家的鼠标事件,实现打开菜单的逻辑。游戏的服务端作为Subject,带有@EventHandler注解的方法成为了Observer,每当Subject中产生一个PlayerInteract事件时,就会将其“通知”给Observer,然后执行我们的业务代码。
  8. 策略模式。游戏中的每个菜单窗口界面都是一个类,通过玩家的openInventory方法可以实现相互替换,这里即策略模式。

  9. 外观模式。为了隐藏游戏系统的复杂性并便于客户端调用,提供了一个系统的接口Player,便于操作玩家执行各种动作。

    可以看到游戏系统非常复杂,但对外只需调用Player中的方法即可实现大多数功能(必要时可以直接调用系统内部的方法),所以外观模式在通常情况下具有便捷性,而又不完全封装,必要时可以直接调用内部系统的方法。


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM