软件设计与体系结构⼤作业-设计模式
代码地址:
设计模式:
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设计。pizza
7. 观察者模式。不同于传统前端,我们⼩组的前端采⽤minecraft plugin的模式。为了与游戏交互,需要监听游戏中产⽣的各种事件,⽽这就需要我们使⽤观察者模式,通过注册⽤于监听的Listener类,即可实现监听不同的事件,再
去具体实现对应的业务逻辑。
例如OrderListener类中,通过监听玩家的⿏标事件,实现打开菜单的逻辑。游戏的服务端作为Subject,带有@EventHandler注解的⽅法成为了Observer,每当Subject中产⽣⼀个PlayerInteract事件时,就会将其“通知”给Observer,然后执⾏我们的业务代码。
8. 策略模式。游戏中的每个菜单窗⼝界⾯都是⼀个类,通过玩家的openInventory⽅法可以实现相互替换,这⾥即策略模式。
9. 外观模式。为了隐藏游戏系统的复杂性并便于客户端调⽤,提供了⼀个系统的接⼝Player,便于操作玩家执⾏各种动作。
可以看到游戏系统⾮常复杂,但对外只需调⽤Player中的⽅法即可实现⼤多数功能(必要时可以直接调⽤系统内部的⽅法),所以外观模式在通常情况下具有便捷性,⽽⼜不完全封装,必要时可以直接调⽤内部系统的⽅法。