3、装饰模式
装饰模式:
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。
优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点:多层装饰比较复杂。
使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。
注意事项:可代替继承。
例(给人搭配不同的服饰)(普通的继承做法):
我们首先可能想到的做法是:
采用继承,实现了服饰与人的分离,这样也可以完成增加新服饰装扮的业务而不需要修改服饰类(只扩展不修改)
但是采用这种方法我们在客户端装扮时就只能一件一件服饰地给人装饰,而我们应该做的是在内部组装完毕然后再显示出来,即需要把所需的功能按正确的顺序串联起来进行控制(对此我们可以采用装饰模式)。
装饰模式(Decorator):动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
装饰模式结构图如下:
Component是定义一个对象的接口,可以给这些对象动态地增加职责。ConcreteComponent是定义了一个具体的对象,也可以给这个对象添加一些职责。Decorator,装饰抽象类,继承了Component,从外类来扩展Component类的功能,但对于Component来说,是无需知道Decorator存在的。至于ConcreteDecorator就是具体的装饰对象,起到给Component添加职责的功能。
基本代码展示:
abstract class Component
{
public abstract void Operation();
}
class ConcreteComponent : Component
{
public override void Operation()
{
Console.WriteLine("具体对象的操作");
}
}
abstract class Decorator : Component
{
protected Component component;
public void SetComponent(Component component)
{
this.component = component;
}
public override void Operation()
{
if (component != null)
{
component.Operation();
}
}
}
class ConcreteDecoratorA : Decorator
{
private string addedState;
public override void Operation()
{
base.Operation();
addedState = "New State";
Console.WriteLine("具体装饰对象A的操作");
}
}
class ConcreteDecoratorB : Decorator
{
public override void Operation()
{
base.Operation();
AddedBehavior();
Console.WriteLine("具体装饰对象B的操作");
}
private void AddedBehavior()
{
}
}
客户端代码:
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();
Console.Read();
}
实际上装饰模式就是采用SetComponent来对对象进行包装的,这样每个装饰对象的实现就和如何使用这个对象分离开了,每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链当中。
另外,如果只有一个ConcreteComponent类而没有抽象的Component类,那么Decorator类可以使ConcreteComponent的一个子类,同样地,如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而是可以把Decorator和ConcreteDecorator的责任合并成一个类。
例(给人搭配不同的服饰)(装饰模式做法):
代码实现:
using System;
using System.Collections.Generic;
using System.Text;
namespace 装饰模式
{
class Program
{
static void Main(string[] args)
{
Person xc = new Person("小菜");
Console.WriteLine("\n第一种装扮:");
Sneakers pqx = new Sneakers();
BigTrouser kk = new BigTrouser();
TShirts dtx = new TShirts();
pqx.Decorate(xc);
kk.Decorate(pqx);
dtx.Decorate(kk);
dtx.Show();
Console.WriteLine("\n第二种装扮:");
LeatherShoes px = new LeatherShoes();
Tie ld = new Tie();
Suit xz = new Suit();
px.Decorate(xc);
ld.Decorate(px);
xz.Decorate(ld);
xz.Show();
Console.WriteLine("\n第三种装扮:");
Sneakers pqx2 = new Sneakers();
LeatherShoes px2 = new LeatherShoes();
BigTrouser kk2 = new BigTrouser();
Tie ld2 = new Tie();
pqx2.Decorate(xc);
px2.Decorate(pqx);
kk2.Decorate(px2);
ld2.Decorate(kk2);
ld2.Show();
Console.Read();
}
}
class Person
{
public Person()
{ }
private string name;
public Person(string name)
{
this.name = name;
}
public virtual void Show()
{
Console.WriteLine("装扮的{0}", name);
}
}
class Finery : Person
{
protected Person component;
//打扮
public void Decorate(Person component)
{
this.component = component;
}
public override void Show()
{
if (component != null)
{
component.Show();
}
}
}
class TShirts : Finery
{
public override void Show()
{
Console.Write("大T恤 ");
base.Show();
}
}
class BigTrouser : Finery
{
public override void Show()
{
Console.Write("垮裤 ");
base.Show();
}
}
class Sneakers : Finery
{
public override void Show()
{
Console.Write("破球鞋 ");
base.Show();
}
}
class Suit : Finery
{
public override void Show()
{
Console.Write("西装 ");
base.Show();
}
}
class Tie : Finery
{
public override void Show()
{
Console.Write("领带 ");
base.Show();
}
}
class LeatherShoes : Finery
{
public override void Show()
{
Console.Write("皮鞋 ");
base.Show();
}
}
}
我们观察主函数就可以得出:装饰模式实质是一种递归的装饰模式,一层一层地添加装饰达到最终的目的。
实际上在实际应用中Java的I/O流就是一种典型的装饰模式,那么我们想要添加新功能时,只需要实现新的装饰器,然后在使用时,组合进去就可以(动态是手段,组合是目的)。
例(InputStream):
package io_test;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class IOTest_InputStream {
public static void main(String[] args) throws IOException {
DataInputStream din = null;
try {
din = new DataInputStream(
new BufferedInputStream(
new FileInputStream("iotest.txt")
)
);
byte bs[] = new byte[din.available()];
din.read(bs);
String content = new String(bs);
System.out.println("文件内容==="+content);
}finally {
din.close();
}
}
}
例(OutputStream)(新增了自定义的装饰器EncryptedOutputStream):
package io_test;
import java.io.IOException;
import java.io.OutputStream;
public class EncrypteOutputStream extends OutputStream {
private OutputStream os = null;
public EncrypteOutputStream(OutputStream os) {
this.os = os;
}
@Override
public void write(int a) throws IOException {
a=a+2;
if(a>=(97+26)) {
a=a-26;
}
this.os.write(a);
}
}
package io_test;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class IOTest {
public static void main(String[] args) throws IOException {
DataOutputStream dout = new DataOutputStream(
new BufferedOutputStream(
new EncrypteOutputStream(
new FileOutputStream("iotest.txt"))));
dout.write("abcd".getBytes());
dout.close();
}
}
iotest.txt文件下就会输出:cdef(改造之前应该为abcd)