一、装饰模式
1、概述
动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
- ConcreteComponent :是定义了一个具体的对象,可以给这个对象添加一些职责;
- Decorator :装饰抽象类,继承了Component,从外部类来扩展Component类的功能,但对于Component来说,无需知道Decorator的存在的;
- ConcerteDecorator :就是具体的装饰对象,起到给Component添加职责的功能;
2、代码演示模块:
我们这里就用给一个对象穿衣服的例子,来说明装饰模式:
(1)Person类(Component)
public class Person {
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public void show() {
System.out.println("装扮的"+ name);
}
}
(2)饰类(Decorator)
public abstract class Finery extends Person {
//人对象作为属性
private Person component;
//get和set方法
public Person getComponent() {
return component;
}
public void setComponent(Person component) {
this.component = component;
}
//重写父类的某个功能
public void show(){
if (component != null){
component.show();
}
}
}
(3)具体服饰类(ConcreteDecorator)
public class Suit extends Finery {
public void show() {
System.out.print("T恤");
super.show();
}
}
public class TShirts extends Finery {
public void show(){
System.out.print("运动裤");
super.show();
}
}
.
.
.
public class GymShoes extends Finery {
public void show() {
System.out.print("球鞋");
super.show();
}
}
(4)客户端代码:
public class test {
public static void main(String[] args) {
Person person = new Person("某某");
System.out.print("第一种装扮:");
Finery ts = new TShirts();
Finery bt = new BigTrouser();
Finery gs = new GymShoes();
ts.setComponent(person);
bt.setComponent(ts);
gs.setComponent(bt);
gs.show();
System.out.print("第二种装扮:");
Finery ne = new Necktie();
Finery su = new Suit();
Finery ls = new LeatherShoes();
ne.setComponent(person);
su.setComponent(ne);
ls.setComponent(su);
ls.show();
}
}
运行结果:
第一种装扮:球鞋 垮裤 大T恤 装扮的某某
第二种装扮:皮鞋 西装 领带 装扮的某某
3、优点
(1)增强功能的灵活性:
装饰模式可以在不改变原有对象结构的基础上,动态地给对象添加新的功能。例如,在一个咖啡店的点单系统中,有一个基础的咖啡类Coffee
,它有一个方法cost()
用于计算咖啡的价格。通过装饰模式,可以创建诸如MilkDecorator
(牛奶装饰器)、SugarDecorator
(糖装饰器)等装饰类。这样,用户可以根据自己的喜好选择添加牛奶或糖来装饰咖啡,而不需要改变Coffee
类本身的结构。如果后续还想添加新的配料,如焦糖、香草等,只需要创建新的装饰类即可。
(2)实现多层装饰组合
可以对一个对象进行多层装饰,以实现复杂的功能组合。比如,对于上面的咖啡示例,可以先使用MilkDecorator
装饰Coffee
对象,然后再使用SugarDecorator
进行装饰。这样就可以得到一杯加了牛奶和糖的咖啡。每一层装饰都可以独立地添加自己的功能,并且这些功能会按照装饰的顺序依次叠加。这种组合方式非常灵活,可以满足各种复杂的业务需求。
(3)符合开闭原则
该模式对扩展开放,对修改关闭。当需要为对象添加新的功能时,不需要修改原有的类,而是通过创建新的装饰类来实现。这使得系统具有更好的可维护性和可扩展性。以图形绘制系统为例,有一个基本的图形类Shape
,它有一个draw()
方法用于绘制图形。如果要为图形添加新的绘制效果,如阴影、渐变等,可以通过创建ShadowDecorator
、GradientDecorator
等装饰类来实现,而不用修改Shape
类的代码。
4、缺点
增强了系统的复杂度, 随着装饰类的增多,系统的复杂度会逐渐增加。因为每个装饰类都要了解被装饰对象的接口,而且装饰类之间可能会相互嵌套,这使得代码的理解和调试变得困难。例如,在一个大型的电商系统中,对商品价格计算功能进行装饰,可能会有折扣装饰、运费装饰、税费装饰等多种装饰类。如果这些装饰类的逻辑比较复杂,而且相互嵌套,就会给开发人员理解和维护代码带来很大的挑战。
5、总结
(1)装饰模式是为已有的功能动态的添加更多的功能的一种方式,属于开闭原则,对扩展开放、修改关闭,降低代码的耦合性,符合代码的设计原子低耦合高内聚,减少了代码的冗余和复杂度,更加灵活的使用每个模块。
(2)那什么时候适合去使用它呢?
正常不使用装饰模式下: 当系统需要新功能的时候,是向旧的类中添加新的代码。这些新加的代码通常装饰了原有类的核心职责或主要行为,这么做的问题主要是在主类中加入了新的字段,新的方法和新的逻辑,从而增加了主类的复杂度,而这些新加入的东西仅仅只是为了满足一些只在特定情况下才会执行的特殊行为的需要。
(3)在使用装饰模式下: 它是把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,因此,当需要执行特殊行为时,客户代码就可以在运行时根据需要有选则的按顺序使用装饰功能包装对象。
二、职责链模式
1、概述
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
Handler :抽象处理请求类:将自身设为自己的属性,再定义一个解决请求的抽象方法
ConcreteHandler : 具体处理请求类,继承Handler抽象类,重写解决请求的方法。每一个的解决请求的方法体可以相互包含,也可以形成互补的方法。可访问它的后继者,如果可处理该请求,就处理。否则就将该请求转发给后继者;
2、代码演示模块
我们处理一组数据,演示职责模式。
(1)Handler类,定义一个处理请求的接口
abstract class Handler {
protected Handler successor;
public void setSuccessor(Handler successor){
//设置继承者
this.successor = successor;
};
//处理请求的抽象方法
public abstract void handleRequest(int request);
}
(2)ConcreteHandler1,当请求数在0-10之间则有权处理,否则转到下一位
public class ConcreteHandler1 extends Handler{
public void handleRequest(int request){
//0-10处理请求
if(request>=0&&request<=10){
System.out.println("ConcreteHandler1 handle request "+request);
}else{
if(successor!=null){
//转移到下一位
successor.handleRequest(request);
}
}
}
}
(3)ConcreteHandler2,当请求数在10-20之间则有权处理,否则转到下一位
public class ConcreteHandler2 extends Handler{
public void handleRequest(int request)
{
if(request >= 10 && request < 20)
{
System.out.println("ConcreteHandler2 处理请求:" + request);
}
else if(successor != null)
{
successor.handleRequest(request);
}
}
}
(4)ConcreteHandler3,请求20-30之间有权处理,否则转到下一位
public class ConcreteHandler3 extends Handler{
public void handleRequest(int request)
{
if(request>=20&&request<30)
{
System.out.println("---由第三个处理者处理---");
}
else
{
if(successor!=null)
{
successor.handleRequest(request);
}
}
}
}
(5) 客户端代码
public class Start {
public static void main(String[] args) {
// 创建一个请求
int request = 5;
// 创建三个处理者
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
Handler handler3 = new ConcreteHandler3();
// 设置处理者链
handler1.setSuccessor(handler2);
handler2.setSuccessor(handler3);
// 处理请求
int [] result ={2,5,14,22,18,3,27,20};
for (int i=0;i<result.length;i++){
handler1.handleRequest(result[i]);
}
}
}
3、优点
(1)降低了发送者和接收者之间的耦合
在责任链模式中,请求的发送者不需要知道具体是哪个接收者来处理请求。它只需要将请求发送到责任链的起始端即可。例如,在一个投诉处理系统中,用户提交投诉后,投诉请求会沿着预先设定的责任链(可能是客服代表 - 客服主管 - 部门经理)传递,用户不需要了解具体是哪个环节会处理他的投诉,这样就降低了发送者和接收者之间的耦合度。
(2)增强系统的灵活性和可扩展性
新的处理者可以很容易地添加到责任链中。
4、缺点
(1)部分请求可能未被处理
如果责任链没有正确配置,或者没有合适的处理者能够处理某个请求,这个请求可能会一直传递到责任链的末尾而得不到处理。比如,在一个数据验证系统中,如果没有对所有可能的数据类型都配置相应的验证处理者,那么一些特殊类型的数据可能就无法得到验证,从而导致数据错误进入后续流程。
(2)调试困难
由于请求在责任链中可能经过多个处理者,当出现问题时,确定是哪个处理者导致的问题可能会比较复杂。例如,在一个复杂的工作流审批系统中,如果一个审批请求出现异常,可能需要逐一检查责任链中的每个审批者的处理逻辑和状态,才能找到问题所在。
5、总结
责任链模式提供了一种灵活的方式来处理请求,使得多个对象可以有机会参与到请求的处理过程中。通过解耦请求发送者和接收者,它增强了系统的可维护性和可扩展性。然而,使用这种模式也需要注意其潜在的缺点,如调试困难、可能出现请求未被处理的情况以及性能问题。