轻松理解装饰器模式

205 阅读4分钟

1、java的功能增强

讲装饰器模式之前,我们先来思考一个问题,如何对java对象进行功能增强?

绝大多数人在初学java时面对这个问题时,第一时间想到的便是继承,子类通过继承父类,在父类的基础上增加一些自己的功能,就可以完成最简单的增强。

实际上java用来做功能增强的方式有三种:
1.继承
2.装饰器模式
3.代理模式

本章节就为何要使用装饰器模式进行讲解。

2、继承

这确实是一种增强的模式,我们先来看看代码怎么写:
回到之前我们举的经典例子,有一个被叫做Liunan的程序员 ,实现了一个叫做Coding的方法
public class Liunan{
    public void coding()
    {
        System.out.println("writing.");
    }
}

此时Liunan收了一个徒弟,他的徒弟除了要自己写代码以外,还要组织代码review,那么我们可以实现一个子类,来完成功能的增强

public class SubLiunanForReview extends Liunan
{
    public void organize()
    {
        System.out.println("review code.");
    }
}

这时Liunan又收了一个徒弟,需要负责review, commit, publish,那么我们可以让这个徒弟完成SubLiunanForReview 的继承,并在其基础上增加commit 和 pulish 方法即可

public class SubLiunanForCommitAndPublish extends SubLiunanForReview
{
    public void commit()
    {
        System.out.println("commit.");
    }

    public void publish()
    {
        System.out.println("publish.");
    }
}

但是如果Liunan再次收徒(Liunan的徒弟好多),此时需要完成的任务又发生了变化,比如我们只要求可以进行commit 和 review,不需要完成publish,按照继承的思路,我们又需要创建新的子类完成功能的增强

因此在继承时我们不能完成:

  • 子类只能增强指定的功能,并且无法动态发生变化;
  • 如果父类是带有数据、信息、属性的话,那么子类无法增强。

3、装饰器模式

先不用纠结这个名词,回到刚才的问题,Liunan有三个徒弟,分别需要实现coding、review、commit、publish四种功能,若使用继承来实现,那么我们将出现类爆炸的模式,因此我们可以换一种思路,既然这四类功能都是组合的关系,且都需要实现coding方法(因为我们是程序员啦~)

那我们先创建一个接口

public interface IProgramer
{
    void coding();
}

然后以Liunan作为基本的IProgramer实现类

public class Liunan implements IProgramer
{
    public void coding()
    {
        System.out.println("liunan is writing.");
    }
}

此时我们创建一个“包装”,这个包装是一个抽象类,它实现了IProgramer接口,并完成了对基础类Programer的包装

public abstract class ProgramerDecorator implements IProgramer
{
    private IProgramer programer;

    public ProgramerDecorator(IProgramer programer)
    {
        this.programer = programer;
    }

    @Override
    public void coding()
    {
        programer.coding();
    }
}

然后根据ProgramerDecorator提供基础的功能扩展,分别是review:

public class ProgramerReview extends ProgramerDecorator
{
    public void review()
    {
        System.out.println("review.");
    }

    @Override
    public void coding()
    {
        super.coding();
        review(); //对coding进行增强
    }

    public ProgramerReview(IProgramer programer)
    {
        super(programer);
    }
}

Commit:

public class ProgramCommit extends ProgramerDecorator
{
    public void commit()
    {
        System.out.println("commit");
    }

    @Override
    public void coding()
    {
        super.coding();
        commit();
    }

    public ProgramCommit(IProgramer programer)
    {
        super(programer);
    }
}

Publish:

public class ProgramPublish extends ProgramerDecorator
{
    public void publish()
    {
        System.out.println("publish.");
    }
        
    @Override
    public void coding()
    {
        super.publish();
        review();
    }

    public ProgramPublish(IProgramer programer)
    {
        super(programer);
    }
}

然后我们来思考,我们要实现

    public static void decorator()
    {
        System.out.println("----only write.");//程序员只写代码
        IProgramer programer = new Liunan();
        programer.coding();

        System.out.println("----write and commit.");//程序员写完代码之后提交

        IProgramer programer1 = new ProgramCommit(new Liunan()); 
        programer1.coding();

        System.out.println("----write and commit and pubish review.");//程序员写完代码后提交,发布,再review。
        IProgramer programer2 = new ProgramerReview(new ProgramPublish(new ProgramCommit(new Liunan())));
        programer2.coding();

    }

执行结果:

此时如果想进行任务的顺序调整,我们只需要修改Program的创建顺序,就能完成,而不需要再进行代码的编写。 代码如下:

    public static void decorator()
    {
        System.out.println("----only write.");
        IProgramer programer = new Liunan();
        programer.coding();

        System.out.println("----write and commit.");

        IProgramer programer1 = new ProgramCommit(new Liunan());
        programer1.coding();

        System.out.println("----write and commit and pubish review.");
        IProgramer programer2 = new ProgramerReview(new ProgramPublish(new ProgramCommit(new Liunan())));
        programer2.coding();

        System.out.println("----write and review and commit publish.");
        IProgramer programer3 = new ProgramPublish(new ProgramCommit(new ProgramerReview(new Liunan())));
        programer3.coding();
    }

执行结果:

总结 以上,我们对装饰器模式做一个总结:

  • 装饰器与基本功能类需要实现同一个接口;

  • 装饰器可对所有实现了此接口的类进行增强,包含它自己;

  • 不同的功能可以抽象成独立的装饰器,在装饰器中对需要增强的方法进行功能增强,如本题中,ProgramPublish装饰器增强的对象为Liunan,增强的功能为publish,我们可进一步对ProgramPublish继续进行增强。我们包装完还是IProgram类型,无论包装多少层,返回的对象都是is-a的关系;

4、代理模式

待补充。

原创文章,转载请注明出处,多谢。