设计模式
2022-05-05 17:27:09 0 举报
AI智能生成
设计模式
作者其他创作
大纲/内容
一个软件实体应当对扩展开放,对修改闭合
开闭原则
任何能使用父类的地方,一定能使用子类
里氏代换原则
要依赖于抽象,不要依赖于实现
抽象不应该依赖于细节,细节应该依赖于抽象
依赖倒转原则
尽量使用合成聚合而不是继承去实现复用
合成聚合复用原则
一个软件实体应该尽可能少的和其他实体发生相互作用
迪米特法则
应该为客户提供尽可能小的单独的接口,而不应该提供大的综合性的接口
接口隔离原则
设计原则
生成器模式(Builder Pattern)是一种设计模式,又名:建造模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),是这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。(维基百科)
内容
建造者模式也叫作生成器模式。建造者模式是一种创建型模式。概念中说这种设计模式可以将复杂对象的构建过程抽象出来。也就是说共同特点的一类产品,我们将他们的构造过程提出来,然后让不同的子类去实现这个构造过程。产生不同的产品。这就是建造者模式,它类似于一个对象工厂,通过不同的构造过程产生不同的对象。这个和工厂模式比较类似,但也有区别。
解析
概念
当一个产品的创建过程非常复杂,比如过程中所需要的一些对象不容易获取到,或者不同的创建过程会产生不同的结果的时候,可以考虑使用建造者模式。建造者模式使用的频率不是很高。
java君所在的公司没有一个汉堡店,只卖鸡排堡和鸡腿堡。但是他们的原料很神奇,在汉堡中放的顺序不同,就会产生不同的口味。所以深受大家的喜欢。汉堡店店长希望可以造出来一个系统,这个系统可以生产出各种各样的鸡排堡和鸡腿堡。这种情况就属于产品的生产过程比较复杂,需要再生产的时候调用原料生产的方法,并且不同的调用顺序还会产生不同的产品。
我们可以看到,使用建造者模式,很完美地生产出了四种汉堡。我们先定义了一个抽象产品类,在这个抽象产品类中有具体的建造产品的方法,在这个方法中有固定的步骤,然后这些固定的步骤在不同的子类中是不同的。这里用到了模板方法模式,然后具体的产品类实现了自己特定的放入食材的方法,比如鸡腿堡放鸡腿,鸡排堡放鸡排。然后我们定义了一个抽象的建造者,其中声明了构建的方法也就是设置生产说明书,因为我们创造一个汉堡是对照生产说明书来的。还有抽象的得到汉堡的方法,在具体的两类汉堡中我们实现各自的生产汉堡的方法。其实就是调用了各个汉堡实例的setSequence方法进行说明书传入。最后有一个指挥类,在指挥类中,我们实现了四种汉堡的实现方法。每种获取方法都是先构造说明书,然后再向具体的构建者传入说明书。然后调用获取该汉堡的方法。调用该汉堡店的make方法进行制作。
建造者模式示例代码
解决方案
想象一个场景
应用场景
比如例子中的ChickenChop和Drumstick类
这个类是具体的产品,一般产品类实现了模板方法模式。这个类就是具体需要被创建的类,其中有自己的业务逻辑。
Product产品类
比如例子中的HamburgerBuilder类
该类是产品的抽象建造者。其中一般声明有两个方法,产品类的内部逻辑处理和组件产品。这是一个产品构建规范,由子类去实现。
Builder建造者类
比如例子中的DrumstickBuilder类和ChickenChopBuilder类。
具体的建造者类,该类实现了Builder抽象类,可以具体返回一个组建好的对象。
ConcreteBuilder类
比如例子中的Director类。
该类负责安排不同的建造顺序。然后调用Builder的buildProduct方法获取产品。
Director指挥类
角色
应用建造者模式进行产品的构建,使得各类的职责明确,符合单一职责原则。而且比较容易扩展。客户不需要知道产品的内部组成细节。比如例子中我们就不需要去关心一个汉堡的内部生产的具体细节。而只关心各部件最后的组合。不同的建造者之间相互独立。容易扩展新的产品。
如果产品的内部过于复杂,会产生需要定义很多具体建造者来适应这些变化的情况,导致系统变的很庞大而不容易管理。
缺点
装配
建造者模式比较注重不同之间的零件之间的装配,前提是这些零件的生产过程已经有了。
创建
而工厂模式注重怎么去创建这些不同的零件。
区别
和工厂模式很相似
关于建造者模式的扩展,我们举了和模板模式结合使用的例子,在产品类之间使用模板方法的模式,在生产不同的产品的时候,零件的组合方式十分重要。而引入建造者模式就很好地实现了这个效果。
总结
7.建造者模式
工厂方法模式(Factory method Pattern)是一种实现了’工厂‘概念的设计模式。就像其它创建型模式一样,她也是在处理不确定对象具体类型的情况下,创建对象的问题。工厂方法的实质:定义一个创建对象的接口,但让实现这个接口的类来决定实例哪个类。工厂方法让类的实例化推迟到子类中进行。(维基百科)
工厂方法模式是一种创建型模式。这个模式提供了一个接口,让实现这个接口的类决定实例化哪一个类,也就是说工厂方法模式将对象的调用者和对象的创建进行分离。调用者不用考虑对象是怎么被创建的。
在所有我们需要得到一个对象实例的地方。都可以考虑使用工厂方法模式,作为new的代替品,如果需要获得不同种类的实例,可以用该模式提供一个统一的接口,调用者通过该接口获取实例,而不用关心对象的实例化过程。
java君所在的餐厅最近推出了几款披萨,分别是培根披萨,鲜虾披萨,火腿披萨。在客人点披萨的时候,服务员会通知厨师去做一个披萨,然后服务员上菜。服务员不用去管比萨是怎么做出来的。他只知道将客人的需求递给厨师,剩下的由厨师来完成就可以。
从上面的例子中,我们定义了产品接口,然后三种产品类去具体实现该接口,还定义了一个工厂接口,然后一个具体的工厂去实现了该接口。我们在场景类中实例化这个具体的工厂。然后如果想获取某个实例,就去调用了createPizza方法并传入一个我们需要的类型的class类即可。这样就相当于服务员向厨师,也就是工厂类请求了一个pizza,传入pizza的要求,然后厨师返回给服务员,服务员上菜。服务员不用了解pizza具体是怎么做出来的,只需要命令厨师,做出来什么什么样的比萨即可。
代码实现
典型应用
比如例子中的Pizza类
可以是接口或者抽象类,定义了产品的业务逻辑。
Product抽象产品
比如上例中三种具体的披萨
实现了具体的产品业务逻辑。可以有多个。
ConreteProduct具体产品
比如上例中AbstractPizzaFactory
声明了构建类的方法.
Factory工厂
Concrete具体工厂
子主题 1
获取一个对象只需要知道这个类的约束条件即可.
降低了不同模块之间的耦合
例子中的工厂代码根本就不需要修改.
优点
简单工厂模式
工厂方法模式也可以直接代替单例模式进行使用.
分支主题
概要
8.工厂模式
设计方案F4
产品的继承结构
产品等级
就是在同一个工厂中生产的一族产品
产品族
使用场景
注意
9.抽象工厂模式
我们在Bed类中定义了一个静态的私有的Bed实例.然后定义了一个静态的getBed()方法.调用该方法就会获得之前的实例.
子主题 3
Spring的javabean默认是单例的
servlet也是单例的
具体场景
扩展困难
单例模式对测试不友好
10.单例模式
原型模式
创建模式
装饰者模式,是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。就功能而言,装饰者模式相比生成的子类更加灵活,这样可以给某个对象而不是整个类添加一些功能。
顾名思义,是用来做装饰的。既然是装饰,就要有被装饰的组件。我们平常的一些东西,被装饰后,通常会在保留之前功能的基础上,有了新功能,有了新功能,就是装饰的意义。在原来的基础上扩展,这种看起来像是继承可以完成的工作。可是概念中说到,装饰者模式比生成子类更加灵活,可以给某个对象而不是整个类添加一些新的功能。可见,使用装饰者模式和直接使用继承是不一样的。
当需要扩展一个类的功能,或者给一个类增加附加职责的时候,或者需要支持功能的动态撤销和添加的时候可以使用该场景
java君喜欢吃东西,而且餐厅老板允许他自己定做,可是java君很讲卫生,还讲究食物色香味俱全。有一次他想吃甜点了,他就想去定做一个蛋糕,餐厅已经有一套做蛋糕的流程了。但是他想要在吃cake之前,用紫外线灯照射厨房3个小时。餐厅的工作人员说这个好办。我们再写一个类,然后继承之前的流程,加上一个照射紫外线的新功能就可以。这个类刚写好,java君又想在做好cake之后在cake上面放一些花瓣。他们就继承了原来的加了紫外线照射的流程后加上放花瓣的新功能,这样就产生了两层继承关系。这个以后如果java君还想再添加新功能,又需要继承,这一层层的继承,维护起来很麻烦。牵一发而动全身,耦合性很强。而且这一次如果java君不想照射紫外线,而是想用消毒水,这下子又需要再写一个继承了,然后放花瓣的类又需要重新继承消毒水。我猜想谁都不想去维护这些类,这样显然是不行的。其实,当你的继承超过两层的时候,就要考虑下是不是设计有问题了。
我们可以用装饰者模式。在一个功能的基础上添加新功能,除了继承,我们还可以使用组合。即把A类当做B类的一个属性,在B类中使用A类的方法。从而实现B类对A类功能的继承。这个就是装饰者模式的基础。
最后的结果中,先对厨房进行了三个小时的紫外线照射,然后进行cake的制作,然后在使用花瓣进行装饰,最后开动。
在此例当中,我们没有使用多重继承来添加多重的行为装饰,而是使用了组合的方式,把类A作为类B的一个属性,然后在B中使用A的方法和属性,这就相当于让B拥有了A的能力。这种组合的方式相比继承更加灵活。这也是装饰者模式的核心。
解决之道
在path下的File中读出数据封装成一个FileputStream流,然后被InputStreamReader装饰成一个Reader流,然后被BufferReader装饰成一个带缓冲的Reader然后处理最后装饰成的这个对象。每一次封装都是对原始流的一次装饰,动态的向原始的文件流中添加职责。最后成为一种我们很方便处理的流。
BufferReader bf =new BufferReader(new InputStreamReader(new FileInputStream(new File(path))))
IO流
经典场景
例如上面的Meal类,这个就是被装饰者的抽象。
抽象组件,是原始的最核心的接口。
其中声明了核心的Operation方法
Component接口
例如上面的cake类
这个类实现了Component接口,是具体的被装饰组件。这个是最核心,最原始,最基本的组件。是具体的被装饰的组件。它实现了Component接口中的核心Operation方法。
ConcreteComponent类
例如例子中的抽象类Decorator
抽象类,是所有装饰者的父类,他实现了Component接口。
它里面是一个私有的Component类型的属性。在构造方法中初始化这个属性。
在实现Component的Operation方法的时候调用的是他的Component属性的Operation方法。
Decorator类
这个是具体的装饰者类。继承自Decorator类。
并且在每一个ConcreteDecorator里有一个私有的新功能的方法。
然后在重写父类的Operation方法的时候。可以根据新添加的方法的执行位置,在super.Operation()之前或者之后调用本类的修饰方法。灵活地向被装饰类中添加新功能。就像上面的PetalDecorate和UItravioletRayDecorate
ConcreteDecorator类
装饰者模式放弃了多重继承,而选择用组合的方式进行职责扩充。
这样降低了耦合,提高了系统的可维护性。
如果再想进行扩展。只需要在写一个新的继承自Decorate的装饰者类即可。
而不同的装饰者之间,没有像用继承那样的强耦合性。灵活性比较强
可以让装饰类和被装饰类独立发展而不会相互影响,降低了他们之间的耦合性。
装饰者模式可以很方便的动态扩展一个实现类的功能。
我们在使用装饰者模式的时候应尽量减少装饰类的数量,降低系统复杂度。
多层的装饰复杂度提高,不利于debug。
类图
5.装饰模式
门面模式
合成模式
代理模式
适配器模式
桥梁模式
共享元类模式
结构模式
分离变化的部分和不变的部分
基于设计原则
以上做法是比较糟糕的,如果再来一个spring先生想要吃aop大餐,就要去改源码了
不符合“对修改关闭,对扩展开放”的原则
子主题 4
变化就是对不同的人要上不同的菜,从代码中抽离,进行分离。
利用多态,给每一个类人一个点某中菜的能力。
我们可以让所有人实现一个Order接口,这个接口中定义了点菜的这个方法,让每个人都去实现这个方法,然后在点菜的时候直接去调用每个人的order方法。
这样在新来人的话,我们不用去修改原来的代码,只需要让新人实现自己的order方法,这样就符合设计原则了
分离变化和不变的东西
如此,无论来汇编先生,还是python女士,都只需要实现Order接口就行了
使代码更具有扩展性,降低了耦合性
举例
你url一大堆,然后由mapping映射获取到指定的action
Struts
springmvc
框架
排序的时候,由于不同的需要排序的类的排序规则不一样,也就是策略不一样,
所以我们把策略封装到客户部分,也就是具体需要排序的类中,也就是让具体需要排序的类实现Comparable接口,
然后在传入到排序的算法中的时候,排序的算法在比较两个元素的先后顺序的时候直接调用其compareTo方法,
根据多态,他调用的其实是具体传入的compareTo方法。也就是封装到子类中的具体的比较策略,这样就能实现了具体的排序
jdk官方排序算法
我们需要对若干个都可以实现我们所要的功能中选择一个最合适的
上下文中有一个策略属性,还有一个设置策略的方法,一个工作方法
工作方法中实现的就是策略的工作方法
上下文类
Context
抽象策略类
Strategy
具体策略类
ConcreteStrategy
策略模式包含的角色
具体的策略A和具体的策略B实现了策略接口,实现了work方法
这样在调用上下文的work方法,就是调用了实际设置的具体策略的work方法
从而实现了将执行策略与具体的策略解耦
有新的策略的时候,只需要再重新声明一个策略类就可以了
关系
实现一族算法,并将每一个算法都封装起来,使其相互交换
策略模式是一种非常简单的设计模式,策略模式是对不同算法的封装,把算法的责任与算法本身分离开来,将算法委派给不同的对象进行管理
因为客户端需要了解不同算之间的区别,选择合适的算法
策略模式中,使用哪一种策略由用户来决定,提高了系统的灵活性,但在一定程度上,也增加了客户端的使用难度
在策略模式中,我们用一个接口代表行为,根据多态,上下文在工作时执行的行为,是这个接口的具体实现
针对接口编程,而不是针对实现编程
传入的比较器是null
元素T也实现了Comparator接口
传入了具体的比较器
JDK官方的排序方法
实现都是直接调用了List的sort方法
提炼总结
关系图
1.策略模式
在这个模式中,算法的步骤是确定的。(对第一句的理解)相当于父类中有算法步骤,但是把具体的算法步骤延迟到子类中去实现。(对第二句的理解)如此:就实现了算法步骤和算法步骤的具体实现分离。如果再有一份新的算法的实现,就再写一个子类就可以了。
既然是步骤,就是有顺序
理解
若干个类中有步骤相同的逻辑,但是步骤的实现方法不同。复杂的算法,可以把核心的算法设计为模板方法,具体相关的细节由子类去实现。
Pascal君和Java君点完餐之后,想要吃饭。他们的吃饭步骤如下:准备餐具,吃饭,收拾餐具。但是pascal君和java君准备的餐具不同,吃饭的方式当然也不一样,并且pascal君不喜欢收拾餐具。而java君每次吃饭后都要自己收拾餐具。
活用场景
分析异同:同:吃饭步骤三步。异:三个步骤的具体实现不同将相同的步骤抽离出来,就属于模板方法。如此:我们将其封装到父类中,这个父类可以是一个抽象类,然后让子类去继承父类,然后子类去具体实现具体的被模板调用的方法,这个就把变化的部分和不变的部分分离开了。
一般将父类的模板方法加上final关键字,防止被恶意覆盖。
父类的抽象方法一般用protected修饰符修饰,让其对子类和同包下的类可见
注意点
servlet中对一个请求进行具体的业务逻辑处理。是调用了HttpServlet中的service方法,然后service方法根据不同的请求分发到具体的方法中,而具体的业务逻辑则由用户去进程HttpServlet类去实现
Servlet
定义了算法的实现步骤
抽象的具体步骤的实现
模板类
子类去继承父类,然后去具体实现步骤
具体实现类
模板方法模式一般分为两类
符合开闭原则
模板方法模式封装了不变的部分,扩展了可变得部分。如果scala君也想要吃饭,只需要去继承父类,然后去实现具体的步骤即可,不需要修改已经存在的代码。
模板方法无法对子类进行约束
在父类中增加is***的方法,然后自己具体实现,返回true或者false表明是否需要调用这个方法。
在父类的模板方法中先判断是否可以执行,然后在执行步骤,这些is***方法就是钩子方法
顾名思义:钩子可以钩取这些基本方法,控制其是否执行。
但是还有一个问题 就是小黄姐姐孩子上学快迟到了 刚洗完的头发还没有来的及吹就出门了 送完孩子之后 她想再去和帅哥约会 就想着去理发店把头发吹干 这个时候 剪和洗都不需要了 那么怎么解决这个问题呢
模板方法模式给出了钩子方法 就是在理发的抽象方法中加上一个is****()的抽象方法 具体再子类中实现 进而判断哪些步骤是否需要执行 就解决了小黄姐姐只需要吹干头发的问题了
钩子方法
问题
当父类有N多步骤,而子类需要实现其中一两步时。
2.模板模式
命令模式将请求封装成一个对象,从而使用不同的请求对客户端进行参数化,使用求情队列,或者记录请求日志,并且支持撤销操作。
从定义上来看,他是把命令封装成了对象,命令就是指令,是上级发给下级的,既然有命令,就会有上下级的层次关系,命令是下发给下层人员去执行的。所以命令模式中有决策者和执行者。
所谓的使用请求将客户端参数化,就是将执行者的命令参数化,也就是将命令封装成了对象。
至于队列和记录日志,就是在基础的命令模式上的扩展
例如:使用遥控控制电视机
我们使用遥控器的时候,做的只是按下某个按钮,然而这个按钮按下之后,遥控器发送的是什么样的红外线信号,我们不得而知,信号传到电视中之后被谁获取了我们也不知道。获取之后有什么样的操作,我们更不得而知。
我们只知道,我们按下这个按钮,电视做出了一些变化,也就是说,我们发出某个指令,获得了一些反馈信息
我们经常要向某些对象发请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么。\t
java君比较勤奋,而且有收拾盘子的好习惯。所以他被餐厅老板奖励,可以让java君去定做一些菜品,这下java君高兴坏了。但不久之后他就发现问题了。比如:java君想吃焗盐鸡。然后他走到餐厅后,找到采购员,然后命令她去采购新鲜的鸡和粗盐。采购回来之后,他又找到负责宰鸡的师傅,帮忙杀掉黄毛鸡,然后找到厨师师傅帮忙给抹上细盐,包上吸油纸,锡纸,埋到粗盐中。最后找到烘烤师傅,帮忙烤熟。这样他才得到一只香喷喷的盐焗鸡。不过这样太麻烦了呀。java君只是一个食客,结果他想吃一只鸡就需要前前后后联系那么多的人。并且java君有时候还想去吃黄焖鸡,这样就要去联系其他的人了。所以,他很厌烦。
java君找到老板说,你这样让我定做虽然好,但整个制作过程都要我去联系,我也受不了啊。你就告诉我一个接头的负责人,我告诉他怎么做。
你们内部怎么处理的我就不管了。最后给我一个完成的菜品就好了。增加一个接头人。java君每次想吃东西的时候就告诉这个接头人。然后等他的实物就可以了。
其 实现 就是将参数命令化,封装成了一个对象。然后传给调用者,让调用者去执行这个命令。这样一来,java君想要什么菜品,只需要实例化一个命令,把他传给调用者就行了。
在实际应用命令模式的时候,Receiver层一般都会被封装
在项目中,我们一般秉承“约定优于配置”这条原则。因为每一个命令都是对一个或者多个Receiver的封装。所以我们可以在项目中通过有意义的类名或者命令处理命令角色和接受角色之间的耦合关系,从而减少高层模块Client类,和底层模块Receiver类的依赖关系,从而提高系统的稳定性
在该例子中,java君并不知道接受者是怎么去做的,也就是Receiver角色并没有暴露给Client,但是Client却依赖了Recevier。
使得请求的发送者和接收者彼此间消除耦合,让对象之间的调用更加灵活
Invoker类
调用者接收客户(客户端)的命令,并且执行该命令
很简单,就是接受命令,执行命令。
Invokeer
执行做饭命令
抽象命令类
执行做盐焗鸡
执行做黄焖鸡
具体命令类
声明需要执行的命令
命令模式的核心。根据具体的需求,Command也可以有多个。所以在类图中,用了抽象Command来表示命令。
Command
就是具体进行工作的角色
员工类
命令传到接收者处,接受者执行命令
因为接收者可能有多个,有多个就要定义为抽象接收者。
一般作为抽象类
Receiver
对命令的封装,将发出命令和执行命令两个责任解耦。每个命令都是一个操作,请求的一方发出命令,然后执行者收到命令,执行命令。
本质
使用命令模式,调用者和接受者之间没有任何依赖关系。调用者进行调用的时候,只需要调用execute()方法即可,而不用关心具体是哪一个接受者
在上面的栗子中,java君如果想要预定其他的菜品,只需要扩展一个子类就可以了。
Command子类容易扩展
只适合接受者是状态的变化,而不适合事件处理。
可以增加一个新的命令rollback,实现事务的回滚。
在每次执行命令行的时候,把执行的过程写到日志中,撤销的时候通过读取日志,执行相反的命令。
一是结合备忘录模式使其恢复到最后状态
子主题 2
命名模式支持撤销操作。
可以使用宏命令,我们在现在的命令模式中执行命令的时候,只是对命令的直接执行。
如果使用宏命令,我们可以在执行命令的时候,传入一个具体的对象,在调用execute方法的时候,会递归调用他所包含的每个成员命令的execute方法。这个对象可以是一个简单的命令,也可以是一个宏命令,这样就会实现对命令的批量处理。
和组合模式进行联合
可以减少Command子类膨胀问题
和模板方法进行联合’
命令模式可和其他模式进行联合
在Command命令复杂的时候,会造成Command子类膨胀
就是把若干个请求封装起来,放到一个队列中。
将请求封装起来后,就可以让请求进行传递,这样不会影响请求的完整性
并且可以让命令在不同的线程中被调用
有一个工作队列,我们在一段添加命令,线程可以在另一端进行不断取出命令来执行。线程和命令之间是完全解耦的。线程不用关心每个命令是干什么用的。他们只知道取出该命令对象,然后调用其execute()方法。
请求队列
有时候我们需要将某些应用的动作记录到日志中。在宕机后,我们可以通过日志,重新调用这些动作,恢复到之前的状态。然后继续执行接下来的动作。
请求日志
命令模式的扩展
3.命令模式
是一种软件设计模式,它包含了一些命令对象和一系列的处理对象。每一个处理对象决定它能出处理哪些命令对象,它也知道如何将它不能处理的命令对象传递给该链中的下一个处理对象。该模式还描述了往该处理链的末尾添加新的处理对象的方法。
责任链模式包含了命令对象和处理对象,是该模式中的两个角色。既然是责任链,那么责任链是怎么表现出来的呢?概念中提到,每一个处理对象决定它能处理的那些对象,这就是责任的体现。我如果能处理这个对象,那么我就对这个对象负责,如果我不能处理,我就把它叫给其他的对象。如此,就形成了一条处理链。
当一个请求可能被多个处理者进行处理时,或者我们不知道会被哪一个处理者进行处理时,就可以使用该模式。
java君太厉害了,以至于java君所在的公司想给他报销餐费,这个公司有个奇怪的规定,项目经理只能报销<=500的餐费,部门经理只能报销<=1000且>=500的餐费,总经理只能报销1000以上的餐费,这一次,java君在享用了自己制定的菜品之后,希望各位领导大大们给他报销,那么大家可能会这样写
如果java君的公司又来了一个新的大爷,也会报销某一个区间内的餐费,这个结构就被破坏了。还需要重新写。
试想
这样大量的if-else语句嵌套,使代码的可读性大大降低了,不美观,体现不出来代码的艺术感,并且如果公司规定变化了。这个if-else结构就得跟着变化。不符合开闭原则。
设计模式就是为了应对需求变更的。并且力求代码的可读性
java君提出一个请求,然后希望得到一个答复。并且是唯一的一个答复,不能是项目经理报销一次后,部门经理再给他报销一次。无论是可不可以被报销,或者被谁报销,java君不会去理会,java君只在意最后的结果。
需求分析
先把java君的请求传给项目经理,能处理则处理,不能处理则交给下一个经理大爷。最后反馈给java君,这样即使又来一个经理大爷,我们直接把他安排到这个处理链的末端就可以了。
设计方案
我们定义了一个抽象的经理类,里面定义了抽象的 该经理是否可以处理该请求,并且定义了具体的处理请求的方法,先判断是否可以被当前经理处理,如果可以,就调用当前经理的处理结果,如果不能处理,就把请求交给他的下一个继任者进行处理。
每个具体的经理类实现自己的判断方法和处理结果
此处有点类似模板方法
我们的请求类java君有一个获取当前请求的金额方法,具体设置请求 请求金额在构造方法中。
例子中的java君
有一个获取请求的方法
设定请求一般在构造方法中进行
有一个设定请求的方法
请求的发出者,希望得到回复
Client
例子中的抽象经理
定义一个请求的处理方法
即设置下一个处理者
定义链的编排
判断能否处理当前请求
具体的处理任务
定义了抽象的由子类实现的两个方法
职责
抽象处理者,负责核心逻辑
包括具体处理者的更换
Handler
例子中的具体经理
具体实现者,实现具体的处理方法,判断是否可以处理该请求
继承自抽象Handler实现了具体的处理任务和判断能否处理当前的请求
Concretehandler
在这条链上,处理器像链表一样,每个处理器都拥有下一个处理器的引用。
一个请求在这个链中传递,被每个处理器处理或者不处理
核心是链
核心
一个请求在一个处理器上被处理,这里是被该处理器处理,然后再交给下一个处理器,被下一个处理器处理。当然,他也可以选择不处理。那么这种一个请求可以被多个处理器进行处理的模式,就叫做不纯的责任链模式。
一个请求之被一个处理器处理
我们在实际项目中用到的大多数是不纯的责任链
设计模式是为项目服务的,项目中怎么用着方便就怎么用,不要被设计模式所限制,造成思维僵化。
将请求和处理分开了,请求者可以不用知道是谁处理的。处理者也可以不知道请求者的全部。两者解耦,提高了 系统的灵活性。
每次请求都是从链头遍历到链尾,造成了性能问题和调试的不方便
一般做法是,在Handler中设置一个最大的节点数量,在setNext中先判断是否超过了这个最大的数量,如果超过了,就不允许建立该节点。
通过模板方法的融合,每个类只需要关注自己的业务逻辑。而不用去考虑其他类的情况,大大增强了系统的可扩展性。
如果想在添加一个处理器,只需要让该处理器实现Handler接口。然后实现自己的处理和判断即可,而不用改动原有的代码。
该模式融合了模板方法,Handler中,抽象父类实现了请求的传递,子类实现了请求的具体处理。
在实际应用中,我们经常回去控制链中节点的数量。避免出现超长的链。
Client封装了一个Command,传给Invoker,Invoker调用Command的执行方法,然后交给Receiver具体执行。如果结合了责任链模式,就可以让这个命令在链中进行传递,让对该命令负责的处理者对其进行处理。从而大大增加了系统的灵活性。
和命令模式结合使用
4.责任链模式
让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能获得的状态创立一个状态类的子类。当系统的状态变化后时,系统便改变所有的子类。(维基百科)
状态模式,可见,有状态的概念。状态就是事物的实时属性,是可以变化的。在程序设计上,状态的变化,意味着行为可能发生变化,这样的话,我们可能会遇到某个行为在不同的状态下会有不同的表现(这个听起来可以用策略模式或者模板方法模式)。在某个状态下进行某个动作之后,还有可能会变化该状态。这样的话,我们可能会需要若干个if-else语句,或者switch语句。这样写就会发生状态的耦合,不利于修改。所以要用状态模式。
当一个对象取决于该对象的状态,状态的改变需要与外部事件进行交互,并且其状态可能被改变的时候,我们就可以使用状态模式。
于是他设计了插卡方法。代码实现F4
只不过这个丑的代码肯定不是出自于作为架构师的java君之手。这个代码,大量的switch-case,不利于扩展。万一领导要添加一个新的需求,添加一个新的状态。比如高级会员卡,这样的话,又需要改。而且有多少个动作就需要改多少个地方。不符合开闭原则,兼职辣眼睛。
java君太厉害了,被餐厅聘请为架构师。java君任职第一天,老板就有了新任务,他们准备添加一台自动售货机,贩卖java咖啡和面向对象速食套餐。而且要插入会员卡进行消费。如此,java君开始设计了。如果这个人没有插卡,是不能买东西的。插卡之后,可以买东西,可以退卡。买完东西可以接着卖可以退卡。退卡之后可以插卡接着买。
该方案我们放弃了大量的if-else或者switch这样的写法,而是采用了一种更为灵活的方式。我们先抽象了一个状态类,这个状态类中定义了插卡,购买,拔卡的这三种方法。然后声明具体的有卡或者无卡状态,让这两个具体状态实现自己状态下的具体方法。其中包括在进行某些动作之后的状态的改变。然后实现自动售货机类,状态类作为他的一个属性,代表他当前的状态,当前状态的set方法中,在设置为传入状态的同时,将当前的自动售货机传给该状态类。以便于状态类可以在适当的时候改变自动售货机类的当前状态。
状态模式代码实现
等待状态
连接状态
断开状态
TCP监听,TCP连接有三个状态。这三个状态按照顺序循环切换,在不同的状态下会有不同的行为。
比如qq用户的状态,有普通用户,会员,超级会员,普通用户可以切换到会员,也可以切换到超级会员。
不过,在具体的项目中,通常不是和TCP的状态这样只能切换到单一的状态,我们经常会遇到的是某个状态可以切换到很多其他的状态
常见场景
如上status类
抽象状态,这个是接口或者抽象状态。封装了具体的状态拥有者,以便完成状态的转换。其中声明了在每个状态下执行的动作方法。
Status
如上的WithCard类和NoCard类。
具体的状态类,这个是状态持有者运行的时候会产生的具体的状态。每一种对象都要去实现相对应的该状态下的具体的动作,如果需要状态转换,则调用父类的状态类拥有者的设置属性的方法。
ConcreteStatus
如上VendingMachine类。
状态拥有者类。也成为上下文或者环境。这个类时状态的具体拥有者。状态是其一个属性。这个类有自己的请求方法,在用到某状态下的动作的时候,调用当前的状态即可。如果改变状态,调用状态的set方法。
在状态模式中,允许一个对象在其内部状态改变的时候改变他的行为,\t看起来像是改变了她的类,其实是动态改变了状态,而在一些状态决定的动作中调用了当前状态的具体实现。这种方式很容易进行扩展,如果有了新的状态,或者代码中包含了大量的和当前状态有关的switch语句或者if-else语句的时候,就可以使用该模式。
避免了大量的switch语句和if-else语句,使得结构更加清晰,避免了程序的复杂,提高了整个系统的可维护性。
如果一个事物有多个状态,就会产生类爆炸。大量的子类使得系统不是那么容易管理。
在不同场景下,策略可以灵活改变。
在策略模式中,我们称之为场景的东西在状态模式中就变成了状态,而所谓的策略,就是不同状态下的动作。
我们可以认为,状态模式就是策略模式的孪生兄弟。
两者共同点
也就是说,在策略模式中,Context上下文类不会被改变。
在策略模式中,不同的场景对应不同的策略。而不同场景之间,通常是不可以相互切换的,他们相对独立。
而在状态模式下,不同状态之间是可以相互切换的,我们可以调用Context的setStatus方法进行状态的切换。从而方便在不同的动作之间进行切换。
两者不同点
状态模式和策略模式很相似
6.状态模式
11.备忘录模式
观察模式
解释器模式
迭代模式
中介模式
访问者模式
行为模式
设计模式
五指记忆法
0 条评论
回复 删除
下一页