装饰者模式
什么是装饰者模式
装饰模式是在不使用继承和不改变原类文件的情况下,动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
这一个解释,引自百度百科,我们注意其中的几点。
1,不改变原类文件。
2,不使用继承。
3,动态扩展。
装饰模式的优点
在讲优点之间,我们知道一个类的功能扩展我们可以通过继承的方式重写父类的方法来达到增强功能。那我们来看下这两种方式。
(1) 类的继承 ( 高耦合,会产生更多的子类,从而引起类的爆炸 )
(2) 对象组合即装饰模式 ( 降耦,不会创造更多的子类 ) 动态的为对象添加功能) 所以类应该对扩展开放,对修改关闭 。
那么总结下装饰者优点:
-
装饰者模式与继承关系的目的都是要扩展对象的功能,但是装饰者可以提供比继承更多的灵活性。
-
通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。
装饰者特点
主要来自一下四点(来自百度百科)
(1) 装饰对象和真实对象有相同的接口。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。
(2) 装饰对象包含一个真实对象的引用(reference)
(3) 装饰对象接受所有来自客户端的请求。它把这些请求转发给真实的对象。
(4) 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。
参与者
1) Component(抽象组件又叫被装饰对象的基类)
定义一个对象接口,可以给这些对象动态地添加职责。
2) ConcreteComponent(具体组件又叫具体被装饰对象)
定义一个对象,可以给这个对象添加一些职责。
3) Decorator(装饰者抽象类)
维持一个指向Component实例的引用,并定义一个与Component接口一致的接口。
4)ConcreteDecorator(具体装饰者)
具体的装饰对象,给内部持有的具体被装饰对象,增加具体的职责。
Component抽象组件
public interface Person {
void eat();
}
ConcreteComponent具体组件
public class Man implements Person {
public void eat() {
System.out.println("男人在吃");
}
}
Decorator装饰者抽象类
public abstract class Decorator implements Person {
protected Person person;
public void setPerson(Person person) {
this.person = person;
}
public void eat() {
person.eat();
}
}
ConcreteDecorator具体装饰者
//具体装饰者A
public class ManDecoratorA extends Decorator {
@Override
public void eat() {
super.eat();
reEat();
}
public void reEat() {
System.out.println("吃点甜点!");
}
}
//具体装饰B
public class ManDecoratorB extends Decorator {
@Override
public void eat() {
super.eat();
reEat();
}
public void reEat(){
System.out.println("喝点汤!");
}
}
测试类
public class Test {
public static void main(String[] args) {
Man man = new Man();
ManDecoratorA md1 = new ManDecoratorA();
ManDecoratorB md2 = new ManDecoratorB();
md1.setPerson(man);
md2.setPerson(md1);
md2.eat();
}
}
结果
男人要吃饭了
喝点汤!
吃点甜点!
Process finished with exit code 0
谈一下自己的理解:
现在有男的要吃饭,吃饭之前可能要洗手、吃点甜点、喝点汤,当然这些动作并不是都必须的,顺序还有可能颠倒。
那么接下来我们来捋一捋这个业务代码的实现思路。
首先我们将动作和实体分开,那么就定义了Man这个实体类和行为接口Person,这个业务的核心是要实现男人吃饭,那么我们让Man去实现Person去实现基本的功能。
接下来,我们吃饭之前可能要吃点甜点、喝点汤、或者喝点汤、吃点甜点。可以这么理解,也就是我们要去修饰这个吃饭的动作,那我们可以这个动作抽象成一个修饰基类,给它不同的实现,并增加特有的方法,吃点甜点喝点汤。
当客户端调用修饰实现类的eat方法的时候,调用顺序永远都是按照我们设置的顺序去调用的。