感谢我院金福生老师的辛勤教学!
做笔记的时候类图大部分是从网络中找的, 很杂, 如不小心侵权, 请联系本人orz
软件体系结构与设计模式
文章目录
大纲
1.基本概念
- 软件体系结构
- 设计模式
- 非功能需求以及含义
- 编程的三个层次/境界
- 设计模式的六大原则
- 面向对象设计的六大原则(举例)
2. 设计模式
- 观察者
- MVC(时序图)
- 策略
- 适配器
- 单例
- 代理
- 工厂/抽象工厂(产品层级,产品族)
- 装饰着
- 命令
- 访问者
3. 软件体系结构
- 分层的风格
- 管道过滤器风格
- 主子程序风格
- 面向对象风格
- 基于共享数据的事件风格
集成开发环境
结合监听器理解时间风格
基本概念
1. 软件体系结构
- 软件体系结构 = (元素,形态,基本理论)
软件能体系结构是一组具有特定结构的设计元素(处理元素、数据元素、连接元素)。 - 软件体系结构涉及 软件高层的设计和实现,通过 组装 一定数量的具有良好形态的 元素,以 满足 主要的 功能和性能需求。
- 定义了组成系统的计算机构件和构件之间的相互关系。
- 体系结构层次的设计:
- 组成系统的构件描述
- 构件之间的交互
- 指导构件交互的模式
- 施加在模式上的约束
- 体系结构层次的设计:
- 是一个系统的结构,包括软件构件、构件的外部可见属性、构件关系
- 外部可见属性:其他构件可以对该构件做的假定(性能特性、错误处理、共享资源的使用等)
- 关注使用、功能、性能、弹性、复用、可理解性、经济和技术约束与折衷、审美考虑
- (IEEE)定义了组成系统的
- 构件(实施计算、保存状态)
- 连接件(表达构件之间的关系)
- 他们之间的匹配(表示系统的拓扑结构)
- 为什么要用软件体系结构
- 随着软件规模和复杂度的增加,设计问题已经超越了算法和数据结构
- 设计和描述系统的总体结构成为一类新问题
2. 设计模式
一套反复被使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码的可靠性。
3. 非功能需求、含义
- 性能
- 可拓展性/伸缩性
- 可维护性
- 可用性
- 安全性
- 业务质量
4. 编程的三个境界(层次)
- 针对实现编程
Dog d = new Dog();
d.bark();
- 针对接口编程 | 超类型编程
Animal animal = new Dog();
animal.makesound();
- 在运行时才指定实现的对象
a = getAnimal();
a.makesound();
5. 设计模式的六大原则
- 单一原则
- 一个类只负责一项职责
- 业务对象(BO)、业务逻辑(BL)拆分
- 里氏替换原则(LSP)
- 子类可以拓展父类的功能,但不能改变原有父类的功能
- (目的:增强健壮性)实际项目中,每个子类对应不同的业务含义,使父类作为参数传递不同的子类完成不同的业务逻辑
- 依赖倒置原则
- 面向接口编程;(通过接口作为参数实现应用场景),抽象就是接口或抽象类,细节就是实现类;
- 上层模块不应该依赖下层模块,两者皆依赖其抽象;细节依赖抽象
接口负责定义public属性和方法,并且申明与其他对象的依赖关系。抽象类负责公共部分的实现,实现类准确的业务逻辑
- 接口隔离
- 建立单一接口;(扩展为类也是一种接口,一切皆接口)
a. 客户端不应该依赖他不需要的接口;
b. 类之间的依赖关系应该建立在最小的接口上
接口的设计粒度越小,系统越灵活,但是灵活的同时结构复杂性提供,开发难度变大,维护性降低
- 建立单一接口;(扩展为类也是一种接口,一切皆接口)
- 迪米特原则
- 最少知道原则,尽量降低类与类之间的耦合;一个对象应该对其他对象有最少的了解
- 开闭原则
- 用抽象构件架构,用实现扩展原则
6. 面向对象设计的六大原则
- 封装变化
- 多用组合少用继承
- 针对接口编程,不针对实现
- 追求交互对象之间的松耦合
- 类对拓展开放,对修改关闭
- 依赖抽象,不依赖具体类
2. 设计模式
1. 观察者
对象之间存在一对多关系时使用。譬如当一个对象被修改时会自动通知其依赖对象
- 关键:在抽象类里有一个arrayList存放观察者们
- 例如:
- 拍卖师观察价格,通知竞价者竞价,竞价者是观察者,拍卖师给出的价格变化,竞价者也做出行动
- 优点
- 观察者和被观察者是抽象耦合的
- 建立了一套触发机制
- 缺点:
- 如果被观察者有很多个直接或间接的观察者,被观察者发生变化要通知到所有观察者将花费大量时间
- 如果被观察对象和观察者之间有循环依赖,观察目标会触发他们之间的循环调用,可能引起系统崩溃
- 观察者模式没有相应的机制让观察者知道被观察对象是如何变化的
Subject.java:
public class Subject {
private List<Observer> observers
= new ArrayList<Observer>();
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
notifyAllObservers();
}
public void attach(Observer observer){
observers.add(observer);
}
public void notifyAllObservers(){
for (Observer observer : observers) {
observer.update();
}
}
}
-----------------------------
ObserverPatternDemo.java:
public class ObserverPatternDemo {
public static void main(String[] args) {
Subject subject = new Subject();
new HexaObserver(subject);
new OctalObserver(subject);
new BinaryObserver(subject);
System.out.println("First state change: 15");
subject.setState(15);
System.out.println("Second state change: 10");
subject.setState(10);
}
}
------------------------------
HexaObserver.java:
public class HexaObserver extends Observer{
public HexaObserver(Subject subject){
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println( "Hex String: "
+ Integer.toHexString( subject.getState() ).toUpperCase() );
}
}
2. MVC(时序图)
模型(存取数据的对象,可以具有逻辑,在数据变化时更新控制器)-视图(模型包含数据的可视化)-控制器(作用在模型和视图上,控制数据流向模型、在数据变化时更新视图,使视图与模型分隔开)
测试类:
public class MVCPatternDemo {
public static void main(String[] args) {
//从数据库获取学生记录
Student model = retrieveStudentFromDatabase();
//创建一个视图:把学生详细信息输出到控制台
StudentView view = new StudentView();
StudentController controller = new StudentController(model, view);
controller.updateView();
//更新模型数据
controller.setStudentName("John");
controller.updateView();
}
private static Student retrieveStudentFromDatabase(){
Student student = new Student();
student.setName("Robert");
student.setRollNo("10");
return student;
}
}
3. 策略
定义一系列算法并把它们封装起来,使他们可以相互替换。在策略模式中,一个类的行为或算法可以在运行时更改。如果一个系统中有许多类,其区别仅在于它们的行为,或者系统需要在几种算法中选择其中一种,就可以使用策略模式。
- 例如:
- 出游方式:自行车、自驾游、坐火车,每一种方式都是一个策略
- 优点:
- 算法可以自由切换
- 避免多重条件判断
- 扩展性良好
- 缺点:
- 策略类增多
- 所有策略类都要暴露出来
接口:
public interface Strategy {
public int doOperation(int num1, int num2);
}
实现接口的实体类:
public class OperationAdd implements Strategy{
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
context类:
public class Context {
private Strategy strategy;
public Context(Strategy strategy){
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2){
return strategy.doOperation(num1, num2);
}
}
测试结果:
public class StrategyPatternDemo {
public static void main(String[] args) {
Context context = new Context(new OperationAdd());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationSubstract());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
context = new Context(new OperationMultiply());
System.out.println("10 * 5 = " + context.executeStrategy(10, 5));
}
}
4. 适配器
适配器模式将一个接口转换成客户需要的另一个接口。使得由于接口不兼容而不能一起工作的那些类可以一起工作。
- 关键:适配器继承或依赖已有的对象,实现目标接口
- 优点:
- 可以让两个没有关联的类一起运行
- 提高类复用性
- 增加类的透明度
- 灵活性好
- 缺点:
- 过多使用适配器会让系统非常零乱,不宜整体进行把握。譬如:明明看到调用a的接口,但是其内部是b接口的实现
- 受java继承机制的影响,最多只能适配一个抽象适配者类
- 分类:对象适配器,类适配器(多重继承)
适配器 | 装饰者 | |
---|---|---|
匹配法则 | 转换接口 | 添加功能 |
代码修改 | 允许客户使用新的库和子集合 | 允许新行为加到类中而无需修改代码 |
创建播放器的接口
public interface MediaPlayer {
public void play(String audioType, String fileName);
}
public interface AdvancedMediaPlayer {
public void playVlc(String fileName);
public void playMp4(String fileName);
}
advance接口的实现类:
public class VlcPlayer implements AdvancedMediaPlayer{
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: "+ fileName);
}
@Override
public void playMp4(String fileName) {
//什么也不做
}
}
public class MediaAdapter implements MediaPlayer {
AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String audioType){
if(audioType.equalsIgnoreCase("vlc") ){
advancedMusicPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer = new Mp4Player();
}
}
@Override
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("vlc")){
advancedMusicPlayer.playVlc(fileName);
}else if(audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer.playMp4(fileName);
}
}
}
另一个接口的适配器类
public class MediaAdapter implements MediaPlayer {
AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String audioType){
if(audioType.equalsIgnoreCase("vlc") ){
advancedMusicPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer = new Mp4Player();
}
}
@Override
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("vlc")){
advancedMusicPlayer.playVlc(fileName);
}else if(audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer.playMp4(fileName);
}
}
}
另一个接口的实体类
public class AudioPlayer implements MediaPlayer {
MediaAdapter mediaAdapter;
@Override
public void play(String audioType, String fileName) {
//播放 mp3 音乐文件的内置支持
if(audioType.equalsIgnoreCase("mp3")){
System.out.println("Playing mp3 file. Name: "+ fileName);
}
//mediaAdapter 提供了播放其他文件格式的支持
else if(audioType.equalsIgnoreCase("vlc")
|| audioType.equalsIgnoreCase("mp4")){
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
}
else{
System.out.println("Invalid media. "+
audioType + " format not supported");
}
}
}
测试
public class AdapterPatternDemo {
public static void main(String[] args) {
AudioPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3", "beyond the horizon.mp3");
audioPlayer.play("mp4", "alone.mp4");
audioPlayer.play("vlc", "far far away.vlc");
audioPlayer.play("avi", "mind me.avi");
}
}
5. 单例
涉及到一个单一的类,该类负责创建自己的对象,同时确保只有一个对象被创建,可直接访问不需要实例化.
- 注意:
- 单例模式只有一个实例
- 单例模式的类必须自己创建自己的唯一实例
- 必须向其他对象提供这一实例
- 优点:
- 在内存里只有一个实例,减少了内存的开销
- 避免对资源的多重占用
- 缺点:
- 没有接口,不能继承,与单一职责原则冲突
- 只关心其内部逻辑,不关心外部的实例化
public class SingleObject {
//创建 SingleObject 的一个对象
private static SingleObject instance = new SingleObject();
//让构造函数为 private,这样该类就不会被实例化
private SingleObject(){}
//获取唯一可用的对象
public static SingleObject getInstance(){
return instance;
}
public void showMessage(){
System.out.println("Hello World!");
}
}
测试
public class SingletonPatternDemo {
public static void main(String[] args) {
//不合法的构造函数
//编译时错误:构造函数 SingleObject() 是不可见的
//SingleObject object = new SingleObject();
//获取唯一可用的对象
SingleObject object = SingleObject.getInstance();
//显示消息
object.showMessage();
}
}
若两个进程同时运行getInstance(),可能会创建多个实例引发错误,解决:
- getInstance()变成同步:
public static synchronized Singleton getInstance(){...}
- 急切创建实例
public class Singleton{
private static Singleton _instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return _instance;
}
}
- 双重检查枷锁
public static Singleton getInstance(){
if(_instance == NULL){
synchronized(Singleton.class){
if(_instance == NULL){
_instance = new Singleton();
}
}
}
return _instance;
}
6. 代理
一个类代表另一个类的功能.为其他对象提供一种代理,控制对这个对象的访问
-
例如:猪八戒去高老庄找高翠兰,把高翠兰的外貌抽象成接口,悟空和高翠兰都实现了这个接口.猪八戒访问高翠兰的时候看不出来这是悟空,可以说,悟空是高翠兰的代理
-
优点:
- 职责清晰
- 高拓展性
- 智能化
-
缺点:
- 由于客户和功能的实现类之间增加了代理,可能造成请求的处理速度变慢.
- 实现代理需要额外的工作,增加了代码的复杂度
(使用代理类减少加载图像的内存占用)
创建一接口
public interface Image {
void display();
}
创建接口实现类(包括代理者和真正实现者)
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName){
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
private void loadFromDisk(String fileName){
System.out.println("Loading " + fileName);
}
}
// 代理者
public class ProxyImage implements Image{
private RealImage realImage;
private String fileName;
public ProxyImage(String fileName){
this.fileName = fileName;
}
@Override
public void display() {
if(realImage == null){
realImage = new RealImage(fileName);
}
realImage.display();
}
}
测试:
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ProxyImage("test_10mb.jpg");
// 图像将从磁盘加载
image.display();
System.out.println("");
// 图像不需要从磁盘加载
image.display();
}
}
7. 工厂
这个模式不会向客户端暴露创建的逻辑,仅使用共同的接口指向创建的对象.定义一个创建对象的接口,让子类自己决定实例化哪一个工厂类,创建过程延迟到子类进行
- 关键: 创建过程在子类进行
- 优点:
- 想要创建一个对象,只需要知道其名字就可以了
- 可扩展性高,增加一个新产品,只需要扩展一个新的工厂类就可以
- 创造过程对客户隐藏,调用者只关心产品接口
- 缺点:
- 每增加一个产品都需要增加一个具体类和一个对象实现工厂,增加了类的个数,系统复杂度和类之间的依赖也增加
创建一个接口
public interface Shape {
void draw();
}
创建接口的实现类
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
创建一个工厂
public class ShapeFactory {
//使用 getShape 方法获取形状类型的对象
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
测试
public class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
//获取 Circle 的对象,并调用它的 draw 方法
Shape shape1 = shapeFactory.getShape("CIRCLE");
//调用 Circle 的 draw 方法
shape1.draw();
//获取 Rectangle 的对象,并调用它的 draw 方法
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//调用 Rectangle 的 draw 方法
shape2.draw();
//获取 Square 的对象,并调用它的 draw 方法
Shape shape3 = shapeFactory.getShape("SQUARE");
//调用 Square 的 draw 方法
shape3.draw();
}
}
8. 抽象工厂(产品层级,产品族)
抽象工厂可以看作其他工厂的工厂,接口负责创建相关对象的工厂,不需要指定具体的类,每个生成的工厂都按照工厂模式提供对象
- 关键: 一个工厂里聚合多个同类产品
- 优点:
- 当一个产品族中的多个对象被设定为一起工作时,可以保证客户端始终只用同一个产品族中的对象
- 缺点:
- 扩展困难,要增加一个新的产品,需要在产品族和具体类中都加代码
测试:
public class AbstractFactoryPatternDemo {
public static void main(String[] args) {
//获取形状工厂
AbstractFactory shapeFactory = FactoryProducer.getFactory("SHAPE");
//获取形状为 Circle 的对象
Shape shape1 = shapeFactory.getShape("CIRCLE");
//调用 Circle 的 draw 方法
shape1.draw();
//获取形状为 Rectangle 的对象
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//调用 Rectangle 的 draw 方法
shape2.draw();
//获取形状为 Square 的对象
Shape shape3 = shapeFactory.getShape("SQUARE");
//调用 Square 的 draw 方法
shape3.draw();
//获取颜色工厂
AbstractFactory colorFactory = FactoryProducer.getFactory("COLOR");
//获取颜色为 Red 的对象
Color color1 = colorFactory.getColor("RED");
//调用 Red 的 fill 方法
color1.fill();
//获取颜色为 Green 的对象
Color color2 = colorFactory.getColor("Green");
//调用 Green 的 fill 方法
color2.fill();
//获取颜色为 Blue 的对象
Color color3 = colorFactory.getColor("BLUE");
//调用 Blue 的 fill 方法
color3.fill();
}
}
9. 装饰者
装饰者允许向一个类添加新的功能又不改变其结构,创建一个装饰类用来包装原有的类,在保持类的方法签名完整性的前提下添加了新的功能
- 关键:
- componet类定义为抽象
- 修饰类引用和继承componet,具体拓展类重写父类的方法
- 优点
- 装饰类和被装饰类可以独立发展,不会相互耦合
- 缺点:
- 多层装饰实现比较复杂
创建一接口
public interface Shape {
void draw();
}
创建接口一实现类
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Shape: Rectangle");
}
}
创建抽象装饰类
public abstract class ShapeDecorator implements Shape {
protected Shape decoratedShape;
public ShapeDecorator(Shape decoratedShape){
this.decoratedShape = decoratedShape;
}
public void draw(){
decoratedShape.draw();
}
}
创建实体装饰类
public class RedShapeDecorator extends ShapeDecorator {
public RedShapeDecorator(Shape decoratedShape) {
super(decoratedShape);
}
@Override
public void draw() {
decoratedShape.draw();
setRedBorder(decoratedShape);
}
private void setRedBorder(Shape decoratedShape){
System.out.println("Border Color: Red");
}
}
测试:
public class DecoratorPatternDemo {
public static void main(String[] args) {
Shape circle = new Circle();
ShapeDecorator redCircle = new RedShapeDecorator(new Circle());
ShapeDecorator redRectangle = new RedShapeDecorator(new Rectangle());
//Shape redCircle = new RedShapeDecorator(new Circle());
//Shape redRectangle = new RedShapeDecorator(new Rectangle());
System.out.println("Circle with normal border");
circle.draw();
System.out.println("\nCircle of red border");
redCircle.draw();
System.out.println("\nRectangle of red border");
redRectangle.draw();
}
}
10. 命令
将一个请求封装成一个对象,传给调用对象,调用对象寻找可以处理该命令的合适对象并传命令给他,让他执行
- 关键:三个对象:received真正执行命令的对象;command;invoker调用命令对象的入口
- 优点:
- 降低系统耦合度
- 新的命令很容易加到系统中
- 缺点:
- 可能导致某些系统有过多的具体命令类
创建命令接口
public interface Order {
void execute();
}
创建命令接口的实现类
public class BuyStock implements Order {
private Stock abcStock;
public BuyStock(Stock abcStock){
this.abcStock = abcStock;
}
public void execute() {
abcStock.buy();
}
}
创建请求类
public class Stock {
private String name = "ABC";
private int quantity = 10;
public void buy(){
System.out.println("Stock [ Name: "+name+",
Quantity: " + quantity +" ] bought");
}
public void sell(){
System.out.println("Stock [ Name: "+name+",
Quantity: " + quantity +" ] sold");
}
}
创建命令调用类
public class Broker {
private List<Order> orderList = new ArrayList<Order>();
public void takeOrder(Order order){
orderList.add(order);
}
public void placeOrders(){
for (Order order : orderList) {
order.execute();
}
orderList.clear();
}
}
测试:
public class CommandPatternDemo {
public static void main(String[] args) {
Stock abcStock = new Stock();
BuyStock buyStockOrder = new BuyStock(abcStock);
SellStock sellStockOrder = new SellStock(abcStock);
Broker broker = new Broker();
broker.takeOrder(buyStockOrder);
broker.takeOrder(sellStockOrder);
broker.placeOrders();
}
}
11. 访问者
使用访问者类改变元素的执行算法,使其可以随着访问者的改变而改变
- 关键:在数据基础类里有一个方法接受访问者,并将自身作为参数传给访问者
- 优点:
- 单一职责
- 扩展性
- 灵活性
- 缺点:
- 具体元素对访问者公布细节,违反迪米特原则
- 依赖了具体类,违反依赖倒置原则
- 具体元素变更困难
创建表示元素的接口
public interface ComputerPart {
public void accept(ComputerPartVisitor computerPartVisitor);
}
创建元素的实体类
public class Keyboard implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
创建访问者接口
public interface ComputerPartVisitor {
public void visit(Computer computer);
public void visit(Mouse mouse);
public void visit(Keyboard keyboard);
public void visit(Monitor monitor);
}
创建实现了访问者类的访问者
public class ComputerPartDisplayVisitor implements ComputerPartVisitor {
@Override
public void visit(Computer computer) {
System.out.println("Displaying Computer.");
}
@Override
public void visit(Mouse mouse) {
System.out.println("Displaying Mouse.");
}
@Override
public void visit(Keyboard keyboard) {
System.out.println("Displaying Keyboard.");
}
@Override
public void visit(Monitor monitor) {
System.out.println("Displaying Monitor.");
}
}
测试:
public class VisitorPatternDemo {
public static void main(String[] args) {
ComputerPart computer = new Computer();
computer.accept(new ComputerPartDisplayVisitor());
}
}
软件体系结构
1. 分层的风格
- 优点:
- 支持基于抽象程度递增的系统设计,使设计者可以把一个复杂系统按递增的步骤分解
- 支持功能增强,功能的改变最多影响相邻上下层
- 支持重用,只要上下层的接口不变,同一层的不同实现可以交换使用.
- 缺点:
- 并不是每个系统都可以容易的划分为几个层次
- 很难找到一个合适的,正确的层次抽象方法
2. 管道过滤器风格
filter必须是独立的实体,不了解数据的来源和去向
- 优点:
- 输入输出更容易理解,过程更直观
- 良好的复用支持
- 良好的可维护性和可增强性
- 允许某些专业分析(吞吐量等)
- 允许并发执行
- 缺点:
- 容易导致批处理方式,不适合交互式系统
- filter多余的解析过程会降低性能,增加复杂度
- 有时必须维护两个分离又相关的流间对应关系
- filter之间传输效率低
3. 主子程序风格
- 优点:
- 任务分解成多个易于控制和处理的子程序
- 便于开发和维护
- 适用于较大程序
- 缺点:
- 规模
- 可重用性差,数据安全性差
- 把数据和处理过程分离,数据结构改变时,所有相关的结构都要跟着改变
4. 面向对象风格
- 优点:
- 高度模块化,改变一个不影响其他
- 封装功能,功能和细节装在对象中,只需调用接口
- 代码重用
- 灵活
- 易维护,接近人的思维,易于理解
- 缺点:
- 对象的调用需要知道标识符
- 不同对象对同一对象的调用可能相互影响
5. 基于共享数据的事件风格
- 优点:
- 支持复用,构件通过登记感兴趣的事件被引入系统
- 便于系统变化,构件可容易的升级更换
- 无需事先设定调用顺序
- 异步执行
- 缺点:
- 系统行为难以控制
- 有时需要一个共享数据仓库进行交互,影响系统性能
- 系统验证和调试成问题