设计模式(Java)----装饰模式Decorator

如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响降为最低?

例题1:人及其属性

人有不同国家,如中国人、美国人,如果有属性:高和富,则有组合: 高、富、高富等共3个属性描述。

如果有属性:高、富、帅,则有组合:高、富、帅、高富、高帅、富帅、高富帅等共7个属性描述。𝟏 + 𝒏 + 𝒏 ∗ 𝑪𝒎𝟏 + 𝑪𝒎𝟐 + ⋯ + 𝑪𝒎𝒎 = 𝟏 + 𝒏 ∗ 𝟐𝒎

如果n=2,m=3(高、富、帅),则需要的类是1+2*23=17个 。

用继承来扩展对象的功能导致子类的膨胀。

基础代码

//业务操作接口
abstract class Person{//公共基类,含有公共方法
	String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	public abstract void desc();
}
//主体类:中国人,美国人两个类
class Chinese extends Person{

	@Override
	public void desc() {
		// TODO Auto-generated method stub
		System.out.println(name+"是中国人");
	}
	
}
class America extends Person{

	@Override
	public void desc() {
		// TODO Auto-generated method stub
		System.out.println(name+"是美国人");
	}
	
}
//扩展操作,对各国人操作
class highChinese extends Chinese{
	public void desc() {
		super.desc();
		System.out.println("是high人");
	}
}
class richChinese extends Chinese{
	public void desc() {
		super.desc();
		System.out.println("是rich人");
	}
}
class highrichChinese extends Chinese{
	public void desc() {
		super.desc();
		System.out.println("是high&rich人");
	}
}
class highAmerica extends America{
	public void desc() {
		super.desc();
		System.out.println("是high人");
	}
}
class richAmerica extends America{
	public void desc() {
		super.desc();
		System.out.println("是rich人");
	}
}
class highrichAmerica extends America{
	public void desc() {
		super.desc();
		System.out.println("是high&rich人");
	}
}
public  class Main{
	public static void main(String[] args){
	highChinese hc=new highChinese();
	hc.setName("姚明");
	richChinese rc=new richChinese();
	rc.setName("马云");
	highrichAmerica hra=new highrichAmerica();
	hra.setName("tom");
	hc.desc();
	rc.desc();
	hra.desc();
	}
}

结果为:

姚明是中国人

是high人

马云是中国人

是rich人

tom是美国人

是high&rich人

这样使用继承得到的结果往往是随着需求的变化,子类急剧增多,同时充斥着重复代码,这时候的关键是划清责任。

问题的原因:

除了其中的具体描述,例如:高个,富人等不同,发现所有的属性描述都是一样的方法,出现大量代码冗余。

改进版本一(组合代替继承)

//业务操作接口
abstract class Person{//公共基类,含有公共方法
	String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	public abstract void desc();
}
//主体类:中国人,美国人两个类
class Chinese extends Person{

	@Override
	public void desc() {
		// TODO Auto-generated method stub
		System.out.println(name+"是中国人");
	}
	
}
class America extends Person{

	@Override
	public void desc() {
		// TODO Auto-generated method stub
		System.out.println(name+"是美国人");
	}
	
}
//扩展操作,对各国人操作
class highChinese {
	Chinese person;//组合代替继承
	public highChinese(Chinese person) {
		this.person=person;
	}
	public void desc() {
		person.desc();
		System.out.println("是high人");
	}
}
class richChinese{
	Chinese person;
	public richChinese(Chinese person) {
		this.person=person;
	}
	public void desc() {
		person.desc();
		System.out.println("是rich人");
	}
}
class highrichChinese{
	Chinese person;
	public highrichChinese(Chinese person) {
		this.person=person;
	}
	public void desc() {
		person.desc();
		System.out.println("是high&rich人");
	}
}
class highAmerica {
	America person;
	public highAmerica(America person) {
		this.person=person;
	}
	public void desc() {
		person.desc();
		System.out.println("是high人");
	}
}
class richAmerica {
	America person;
	public richAmerica(America person) {
		this.person=person;
	}
	public void desc() {
		person.desc();
		System.out.println("是rich人");
	}
}
class highrichAmerica{
	America person;
	public highrichAmerica(America person) {
		this.person=person;
	}
	public void desc() {
		person.desc();
		System.out.println("是high&rich人");
	}
}
public  class Main{
	public static void main(String[] args){
		Chinese p=new Chinese();
		p.setName("姚明");
		highChinese hc=new highChinese(p);
		hc.desc();
		p.setName("马云");
		richChinese rc=new richChinese(p);
		rc.desc();
		America q=new America();
		q.setName("tom");
		highrichAmerica hra=new highrichAmerica(q);
		hra.desc();
	}
}

结果为:

姚明是中国人

是high人

马云是中国人

是rich人

tom是美国人

是high&rich人

当一个变量的声明类型都是某个基类(Person)的子类(Chinese, American)的时候,就该将它声明为这个基类(Person),由于多态,可以使得它在未来(运行时)成为子类的对象。

//业务操作接口
abstract class Person{//公共基类,含有公共方法
	String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	public abstract void desc();
}
//主体类:中国人,美国人两个类
class Chinese extends Person{

	@Override
	public void desc() {
		// TODO Auto-generated method stub
		System.out.println(name+"是中国人");
	}
	
}
class America extends Person{

	@Override
	public void desc() {
		// TODO Auto-generated method stub
		System.out.println(name+"是美国人");
	}
	
}
//扩展操作,对各国人操作
class highPerson {
	Person person;
	public highPerson(Person person) {
		this.person=person;
	}
	public void desc() {
		person.desc();
		System.out.println("是high人");
	}
}
class richPerson{
	Person person;
	public richPerson(Person person) {
		this.person=person;
	}
	public void desc() {
		person.desc();
		System.out.println("是rich人");
	}
}
class highrichPerson{
	Person person;
	public highrichPerson(Person person) {
		this.person=person;
	}
	public void desc() {
		person.desc();
		System.out.println("是high&rich人");
	}
}

public  class Main{
	public static void main(String[] args){
		Chinese p=new Chinese();
		p.setName("姚明");
		highPerson hc=new highPerson(p);
		hc.desc();
		p.setName("马云");
		richPerson rc=new richPerson(p);
		rc.desc();
		America q=new America();
		q.setName("tom");
		highrichPerson hra=new highrichPerson(q);
		hra.desc();
	}
}

结果为:

姚明是中国人

是high人

马云是中国人

是rich人

tom是美国人

是high&rich人

发现问题:为了保证从继承转组合以后的抽象接口函数public void desc() 遵循接口规范,即继承基类设置的抽象接口函数public void desc() 。还是需要通过继承来完善接口规范,不过只需要继承基类Person。

//业务操作接口
abstract class Person{//公共基类,含有公共方法
	String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	public abstract void desc();
}
//主体类:中国人,美国人两个类
class Chinese extends Person{

	@Override
	public void desc() {
		// TODO Auto-generated method stub
		System.out.println(name+"是中国人");
	}
	
}
class America extends Person{

	@Override
	public void desc() {
		// TODO Auto-generated method stub
		System.out.println(name+"是美国人");
	}
	
}
//扩展操作,对各国人操作
class highPerson extends Person{//为实现接口继承基类Person
	Person person;//基类代替子类,消除编译时的依赖
	public highPerson(Person person) {
		this.person=person;
	}
	public void desc() {//继承基类Person就能保证实现接口
		person.desc();
		System.out.println("是high人");
	}
}
class richPerson extends Person{
	Person person;
	public richPerson(Person person) {
		this.person=person;
	}
	public void desc() {
		person.desc();
		System.out.println("是rich人");
	}
}
class highrichPerson extends Person{
	Person person;
	public highrichPerson(Person person) {
		this.person=person;
	}
	public void desc() {
		person.desc();
		System.out.println("是high&rich人");
	}
}

public  class Main{
	public static void main(String[] args){
		Chinese p=new Chinese();
		p.setName("姚明");
		highPerson hc=new highPerson(p);
		hc.desc();
		p.setName("马云");
		richPerson rc=new richPerson(p);
		rc.desc();
		America q=new America();
		q.setName("tom");
		highrichPerson hra=new highrichPerson(q);
		hra.desc();
	}
}

结果为:

姚明是中国人

是high人

马云是中国人

是rich人

tom是美国人

是high&rich人

根据重构:当类中含有重复字段和方法,应该将其提到基类中去。

发现问题:这里,highPerson , richPerson 和highrichPerson三个类中都含有字段Person person,应该将其提到基类中去,但是,如果将Person person提到Person基类中去,会发现在Person类中会包含这个不需要的字段。

解决:设计一个中间基类。这样就引出了装饰模式。

改进版本二(使用装饰模式<中间基类>)

//业务操作接口
abstract class Person{//公共基类,含有公共方法
	String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	public abstract void desc();
}
abstract class DecoratorPerson extends Person{
	Person person;//以聚合的方式来支持未来多态的变化
	public DecoratorPerson(Person person) {
		this.person=person;
	}
}
//主体类:中国人,美国人两个类
class Chinese extends Person{

	@Override
	public void desc() {
		// TODO Auto-generated method stub
		System.out.println(name+"是中国人");
	}
	
}
class America extends Person{

	@Override
	public void desc() {
		// TODO Auto-generated method stub
		System.out.println(name+"是美国人");
	}
	
}
//扩展操作,对各国人操作
class highPerson extends DecoratorPerson{//为避免重复,实现接口继承DecoratorPerson
	public highPerson(Person person) {
		super(person);
	}
	public void desc() {//继承基类Person就能保证实现接口
		person.desc();
		System.out.println("是high人");
	}
}
class richPerson extends  DecoratorPerson{
	public richPerson(Person person) {
		super(person);
	}
	public void desc() {
		person.desc();
		System.out.println("是rich人");
	}
}
class highrichPerson extends  DecoratorPerson{
	public highrichPerson(Person person) {
		super(person);
	}
	public void desc() {
		person.desc();
		System.out.println("是high&rich人");
	}
}

public  class Main{
	public static void main(String[] args){
		Chinese p=new Chinese();
		p.setName("姚明");
		highPerson hc=new highPerson(p);
		hc.desc();
		p.setName("马云");
		richPerson rc=new richPerson(p);
		rc.desc();
		America q=new America();
		q.setName("tom");
		highrichPerson hra=new highrichPerson(q);
		hra.desc();
	}
}

结果为:

姚明是中国人

是high人

马云是中国人

是rich人

tom是美国人

是high&rich人

highrichPerson是组合属性,可以用high和rich表示,可以删除

//业务操作接口
abstract class Person{//公共基类,含有公共方法
	String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	public abstract void desc();
}
abstract class DecoratorPerson extends Person{
	Person person;//以聚合的方式来支持未来多态的变化
	public DecoratorPerson(Person person) {
		this.person=person;
	}
}
//主体类:中国人,美国人两个类
class Chinese extends Person{

	@Override
	public void desc() {
		// TODO Auto-generated method stub
		System.out.println(name+"是中国人");
	}
	
}
class America extends Person{

	@Override
	public void desc() {
		// TODO Auto-generated method stub
		System.out.println(name+"是美国人");
	}
	
}
//扩展操作,对各国人操作
class highPerson extends DecoratorPerson{//为避免重复,实现接口继承DecoratorPerson
	public highPerson(Person person) {
		super(person);
	}
	public void desc() {//继承基类Person就能保证实现接口
		person.desc();
		System.out.println("是high人");
	}
}
class richPerson extends  DecoratorPerson{
	public richPerson(Person person) {
		super(person);
	}
	public void desc() {
		person.desc();
		System.out.println("是rich人");
	}
}
public  class Main{
	public static void main(String[] args){
		Chinese p=new Chinese();
		p.setName("姚明");
		highPerson hc=new highPerson(p);
		hc.desc();
		p.setName("马云");
		richPerson rc=new richPerson(p);
		rc.desc();
		America q=new America();
		q.setName("tom");
		highPerson hp=new highPerson(q);
		richPerson rp=new richPerson(hp);
		rp.desc();
	}
}

此时,无论是增加国家还是增加属性只需增加一个类。

设n是国家的个数,m是属性个数,则需要的类的最终个数是:1+n+1+m < 1+n*2m

上面的n=2,m=2所以需要的类是1+2+1+2=6个。

增加一个handsome属性

abstract class Person{//公共基类,含有公共方法
	String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	public abstract void desc();
}
abstract class DecoratorPerson extends Person{
	Person person;//以聚合的方式来支持未来多态的变化
	public DecoratorPerson(Person person) {
		this.person=person;
	}
}
//主体类:中国人,美国人两个类
class Chinese extends Person{

	@Override
	public void desc() {
		// TODO Auto-generated method stub
		System.out.println(name+"是中国人");
	}
	
}
class America extends Person{

	@Override
	public void desc() {
		// TODO Auto-generated method stub
		System.out.println(name+"是美国人");
	}
	
}
//扩展操作,对各国人操作
class highPerson extends DecoratorPerson{//为避免重复,实现接口继承DecoratorPerson
	public highPerson(Person person) {
		super(person);
	}
	public void desc() {//继承基类Person就能保证实现接口
		person.desc();
		System.out.println("是high人");
	}
}
class richPerson extends  DecoratorPerson{
	public richPerson(Person person) {
		super(person);
	}
	public void desc() {
		person.desc();
		System.out.println("是rich人");
	}
}
class handsomePerson extends DecoratorPerson{//为避免重复,实现接口继承DecoratorPerson
	public handsomePerson(Person person) {
		super(person);
	}
	public void desc() {//继承基类Person就能保证实现接口
		person.desc();
		System.out.println("是handsome人");
	}
}
public  class Main{
	public static void main(String[] args){
		Chinese p=new Chinese();
		p.setName("姚明");
		highPerson hc=new highPerson(p);
		hc.desc();
		p.setName("马云");
		richPerson rc=new richPerson(p);
		rc.desc();
		America q=new America();
		q.setName("tom");
		highPerson hp=new highPerson(q);
		richPerson rp=new richPerson(hp);
		rp.desc();
		handsomePerson hsp=new handsomePerson(rc);
		hsp.desc();
		hc=new highPerson(hsp);
		hc.desc();
	}
}

结果为:

姚明是中国人

是high人

马云是中国人

是rich人

tom是美国人

是high人

是rich人

马云是中国人

是rich人

是handsome人

马云是中国人

是rich人

是handsome人

是high人

结果为:

姚明是中国人

是high人

马云是中国人

是rich人

tom是美国人

是high人

是rich人

定义:动态的给一个对象添加一些额外的职责, 就增加功能来说, 装饰模式比生成子类(继承)更为灵活(消除重复代码&减少子类个数)

 

abstract class Component{
	public abstract void Operation();
}
class ConcreteComponent extends Component{

	@Override
	public void Operation() {
		// TODO Auto-generated method stub
		System.out.println("具体对象操作");
	}
	
}
abstract class Decorator extends Component{
	protected Component component;
	public void SetComponent(Component component) {
		this.component=component;
	}
	public void Operation() {
		if(component!=null) {//执行的是成员component的Operation()
			component.Operation();
		}
	}
}
class ConcreteDecoratorA extends Decorator{
	private String addedState;//区别于A的独有方法
	public void Operation() {
		super.Operation();
		addedState="New State";
		System.out.println("具体装饰对象A的操作");
	}
}
class ConcreteDecoratorB extends Decorator{
	private void addedBehavior() {//区别于A的独有方法
		System.out.println("AddedBehavior Operation");
	}
	public void Operation() {
		super.Operation();
		addedBehavior();
		System.out.println("具体装饰对象B的操作");
	}
}
public  class Main{
	public static void main(String[] args){
	ConcreteComponent c=new ConcreteComponent();
	ConcreteDecoratorA d1=new ConcreteDecoratorA();
	ConcreteDecoratorB d2=new ConcreteDecoratorB();
	d1.SetComponent(c);
	d2.SetComponent(d1);
	d2.Operation();
	
	}
}

结果为:

具体对象操作

具体装饰对象A的操作

AddedBehavior Operation

具体装饰对象B的操作

装饰模式常常被称为包裹模式,就是因为每一个具体装饰类都将下一个具体装饰类或者具体构件类包裹起来。

通过采用组合而非继承的手法,Decorator模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了使用继承带来的“灵活性差”和“多子类衍生问题”。

例题2:写一个可以给人搭配不同服饰的程序

abstract class Component{
	public abstract void Show();
}
class Person extends Component{
	public Person() {}
	private String name;
	public Person(String name) {
		this.name=name;
	}
	@Override
	public void Show() {
		// TODO Auto-generated method stub
		System.out.println("装扮的"+name);
	}
	
}
//服饰类
class Finery extends Component{
	protected Component component;
	public void Decorate(Component component) {
		this.component=component;
	}
	@Override
	public void Show() {
		// TODO Auto-generated method stub
		if(component!=null) {
			component.Show();
		}
	}
}
//具体服饰类
class TShirts extends Finery{
	public void Show() {
		System.out.println("大T恤"); super.Show();
	}
}
class BigTrouser extends Finery{
	public void Show() {
		System.out.println("垮裤"); super.Show();
	}
}
class Sneakers extends Finery{
	public void Show() {
		System.out.println("破球鞋"); super.Show();
	}
}
class Suit extends Finery{
	public void Show() {
		System.out.println("西装"); super.Show();
	}
}
class Tie extends Finery{
	public void Show() {
		System.out.println("领带"); super.Show();
	}
}
class LeatherShoes extends Finery{
	public void Show() {
		System.out.println("皮鞋"); super.Show();
	}
}
public  class Main{
	public static void main(String[] args){
		Person xc=new Person("小菜");
		System.out.println("\n第一种装扮");
		Sneakers p=new Sneakers();//生成服饰
		BigTrouser k=new BigTrouser();
		TShirts d=new TShirts();
		//搭配的过程
		p.Decorate(xc);
		k.Decorate(p);
		d.Decorate(k);
		d.Show();
		System.out.println("\n第二种装扮");
		LeatherShoes px=new LeatherShoes();
		Tie l=new Tie();
		Suit x=new Suit();
		px.Decorate(xc);
		l.Decorate(px);
		x.Decorate(l);
		x.Show();
	}
}

结果为:

第一种装扮

大T恤

垮裤

破球鞋

装扮的小菜

 

第二种装扮

西装

领带

皮鞋

装扮的小菜

使用装饰模式主要有以下的优点:

1、装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。

2、通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。

装饰模式缺点:

会产生很多细粒度对象。

装饰模式与策略模式

策略模式改变的是对象的内核

装饰模式改变的是对象的外壳

策略模式一层调用

装饰模式递归调用

可以有机结合

以上部分摘取自朱红梅老师2020年5月的课件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值