复合模式——demo

复合模式就是将前面所学的模式结合起来一起解决问题。复合模式在一个解决方案中通常结合两个或多个模式以解决问题

下面我们用一个例子来说明复合模式的应用

1、我们要让一群鸭子叫,因为叫是鸭子的行为,而且不同鸭子的叫声也不一样,所以我们创建一个公共接口Quackable

package duck;

/**
 * 鸭子将实现这个接口,然后实现各自不同的叫声
 */
public interface Quackable
{
    public void quack();
}

2、某些鸭子实现了Quackable接口

package duck;

/**
 * 绿头鸭
 */
public class MallardDuck implements Quackable {
    @Override
    public void quack() {
        System.out.println("呱呱呱");
    }
}
package duck;

/**
 * 红头鸭
 */
public class RedheadDuck implements Quackable {
    @Override
    public void quack() {
        System.out.println("呱呱呱");
    }
}
package duck;

/**
 * 橡皮鸭
 */
public class RubberDuck implements Quackable {
    @Override
    public void quack() {
        System.out.println("吱吱吱");
    }
}

3、模拟器模拟鸭子叫

import duck.MallardDuck;
import duck.Quackable;
import duck.RedheadDuck;
import duck.RubberDuck;
import goose.Goose;
import goose.GooseAdapter;

public class Test
{
    public static void main(String[] args) {
        Test test = new Test();
        test.go();
    }

   void go()
    {
        Quackable mallardDuck = new MallardDuck();
        Quackable redheadDuck = new RedheadDuck();
        Quackable rubberDuck = new RubberDuck();

        simulate(mallardDuck);
        simulate(redheadDuck);
        simulate(rubberDuck);
    }

    void simulate(Quackable quackable)
    {
        quackable.quack();
    }
}
呱呱呱
呱呱呱
吱吱吱

到目前为止一切顺利。以上是一个非常简单的例子,但是没有用到任何模式。下面我们会逐步增加一些功能

我们希望加入一些其他动物,有鸭子的地方就会有鹅,所以我们添加一个鹅类,鹅是咯咯叫

package goose;

/**
 * 鹅类
 */
public class Goose
{
    public void honk()
    {
        System.out.println("咯咯咯");
    }
}

我们增加了一个鹅类,他不是鸭子类型,所以也不是Quackable类型,也无法传入simulate(Quackable quackable),

我们希望将鹅变成鸭子类型,所以可以用适配器模式,将它适配成鸭子

package goose;

import duck.Quackable;

/**
 * 利用适配器,我们可以将鹅适配成鸭子
 */
public class GooseAdapter implements Quackable {
    Goose goose;

    /**
     * 构造器传入需要适配的对象
     * @param goose
     */
    public  GooseAdapter(Goose goose) {
        this.goose = goose;
    }

    /**
     * 当调用quack方法时,会被委托到鹅的honk方法
     */
    @Override
    public void quack() {
        goose.honk();
    }
}

现在我们的模拟器可以使用鹅了,我们创建鹅对象,将它包装进适配器,以便实现Quackable接口,这样,我们就可以继续了

import duck.MallardDuck;
import duck.Quackable;
import duck.RedheadDuck;
import duck.RubberDuck;
import goose.Goose;
import goose.GooseAdapter;

public class Test
{
    public static void main(String[] args) {
        Test test = new Test();
        test.go();
    }

   void go()
    {
        Quackable mallardDuck = new MallardDuck();
        Quackable redheadDuck = new RedheadDuck();
        Quackable rubberDuck = new RubberDuck();
        //将鹅包装进适配器,就可以让鹅像鸭子一样,拥有鸭子的行为
        Quackable goose = new GooseAdapter(new Goose());

        simulate(mallardDuck);
        simulate(redheadDuck);
        simulate(rubberDuck);
        //因为适配器是包装了鹅,并且实现了Quackable接口,所以能传入
        simulate(goose);
    }

    void simulate(Quackable quackable)
    {
        quackable.quack();
    }
}

 simulate会调用许多对象的quack()方法,其中也包括适配器的quack()方法

呱呱呱
呱呱呱
吱吱吱
咯咯咯

下面我们将有一个新的需求,统计鸭子叫声的次数。怎么才能办到呢?我们可以创建一个装饰者,通过把鸭子包装进装饰者对象,给鸭子赋予新的行为(计算次数的行为),这样做的好处就是不用修改鸭子内部的代码。这就是装饰者模式

下面我们开始创建一个装饰者

package decorator;

import duck.Quackable;

/**
 * 统计鸭子叫声的装饰者
 * 和适配器一样它要实现目标接口
 */
public class QuackCounter implements Quackable
{
    Quackable duck;      //被装饰的对象
    static int count;   //我们用静态变量来跟踪所有呱呱叫次数

    //将被装饰的对象传入构造器中,并记录在实例变量中
    public QuackCounter(Quackable duck) {
        this.duck = duck;
    }

    /**
     * 当quack()被调用时,叫声次数加一
     */
    @Override
    public void quack() {
        duck.quack();
        count++;
    }

    /**
     * 返回所有在Quackable中发生的呱呱叫次数
     * @return
     */
    public static int getCount()
    {
        return count;
    }
}

模拟器

import decorator.QuackCounter;
import duck.MallardDuck;
import duck.Quackable;
import duck.RedheadDuck;
import duck.RubberDuck;
import goose.Goose;
import goose.GooseAdapter;

public class Test
{
    public static void main(String[] args) {
        Test test = new Test();
        test.go();
    }

   void go()
    {
        //每创建一个Quackable,就用一个新的装饰者包装它,我们只统计鸭子的叫声,所以不用装饰鹅
        Quackable mallardDuck = new QuackCounter(new MallardDuck());
        Quackable redheadDuck =  new QuackCounter(new RedheadDuck());
        Quackable rubberDuck =  new QuackCounter(new RubberDuck());
        //将鹅包装进适配器,就可以让鹅像鸭子一样,拥有鸭子的行为
        Quackable goose = new GooseAdapter(new Goose());

        simulate(mallardDuck);
        simulate(redheadDuck);
        simulate(rubberDuck);
        //因为适配器是包装了鹅,并且实现了Quackable接口,所以能传入
        simulate(goose);

        //我们查看鸭子呱呱叫了几次
        System.out.println("鸭子总共叫了" + QuackCounter.getCount()+"次");
    }

    /**
     * 这里没有任何变动,被装饰和被适配的对象,还是Quackable类
     * @param quackable
     */
    void simulate(Quackable quackable)
    {
        quackable.quack();
    }
}
呱呱呱
呱呱呱
吱吱吱
咯咯咯
鸭子总共叫了3次

这个鸭叫计数器很棒,但是我们发现很多鸭子没有被计算进去,因为这些鸭子没有被包装,所以它们没有计数效果,这就是装饰模式的问题!而且每次创建对象都要装饰一边,如果忘记装饰了,就没有统计效果了。所以我们打算将创建和装饰的部分封装起来,这就需要用到工厂模式

所以接下来我们需要创建一个工厂,它专门负责创建装饰过的鸭子,由于此工厂需要创建不同类型的鸭子产品家族,所以要用到抽象工厂模式

首先创建一个抽象工厂

package factory;

import duck.Quackable;

/**
 * 我们定义一个抽象工厂,他们的子类会创建不同的家族
 */
public abstract class AbstractDuckFactory
{
    public abstract Quackable createMallardDuck();
    public abstract Quackable createRedheadDuck();
    public abstract Quackable createRubberDuck();
}

 创建我们真正需要的工厂,他们负责创建被装饰过的鸭子

package factory;

import decorator.QuackCounter;
import duck.MallardDuck;
import duck.Quackable;
import duck.RedheadDuck;
import duck.RubberDuck;

/**
 * CountingDuckFactory扩展自抽象工厂,创建被装饰过的鸭子,它们具有统计的行为
 */
public class CountingDuckFactory extends AbstractDuckFactory {
    @Override
    public Quackable createMallardDuck() {
        return new QuackCounter(new MallardDuck());
    }

    @Override
    public Quackable createRedheadDuck() {
        return new QuackCounter(new RedheadDuck());
    }

    @Override
    public Quackable createRubberDuck() {
        return new QuackCounter(new RubberDuck());
    }
}

再来修改一下模拟器

import decorator.QuackCounter;
import duck.MallardDuck;
import duck.Quackable;
import duck.RedheadDuck;
import duck.RubberDuck;
import factory.AbstractDuckFactory;
import factory.CountingDuckFactory;
import goose.Goose;
import goose.GooseAdapter;

public class Test
{
    public static void main(String[] args) {
        Test test = new Test();
        AbstractDuckFactory countingDuckFactory = new CountingDuckFactory();
        test.go(countingDuckFactory);
    }

   void go(AbstractDuckFactory countingDuckFactory)
    {
        //每创建一个Quackable,就用一个新的装饰者包装它,我们只统计鸭子的叫声,所以不用装饰鹅
//        Quackable mallardDuck = new QuackCounter(new MallardDuck());
//        Quackable redheadDuck =  new QuackCounter(new RedheadDuck());
//        Quackable rubberDuck =  new QuackCounter(new RubberDuck());

        //现在可以利用工厂来创建鸭子对象了
        Quackable mallardDuck = countingDuckFactory.createMallardDuck();
        Quackable redheadDuck = countingDuckFactory.createRedheadDuck();
        Quackable rubberDuck = countingDuckFactory.createRubberDuck();
        //将鹅包装进适配器,就可以让鹅像鸭子一样,拥有鸭子的行为
        Quackable goose = new GooseAdapter(new Goose());

        simulate(mallardDuck);
        simulate(redheadDuck);
        simulate(rubberDuck);
        //因为适配器是包装了鹅,并且实现了Quackable接口,所以能传入
        simulate(goose);

        //我们查看鸭子呱呱叫了几次
        System.out.println("鸭子总共叫了" + QuackCounter.getCount()+"次");
    }

    /**
     * 这里没有任何变动,被装饰和被适配的对象,还是Quackable类
     * @param quackable
     */
    void simulate(Quackable quackable)
    {
        quackable.quack();
    }
}

和上次运行结果一样,这次我们能确定所有鸭子都被装饰过了,因为我们使用的是工厂模式,不需要自己创建对象 

呱呱呱
呱呱呱
吱吱吱
咯咯咯
鸭子总共叫了3次

这次巡逻员又提出了一个问题,我们为什么要个别管理鸭子呢?如果鸭子对像很多,就乱了

 所以我们需要将这些鸭子视为一个集合,我们下一次命令,就能让所有鸭子听命行事,这就太方便了。

我们需要像对待单个对象一样对待集合,这就要用到组合模式。下面我们将创建一个组合类,它需要和节点元素一样实现相同的接口

package compose;

import duck.Quackable;

import java.util.ArrayList;
import java.util.Iterator;

/**
 * 创建鸭子组合类,操作这个组合就像和操作节点元素一样,所以它也和节点元素一样实现相同的接口
 */
public class Flock implements Quackable{
    ArrayList quackers = new ArrayList();

    /**
     * 将节点元素记录到属于Flock的集合中
     * @param quacker
     */
    public void add(Quackable quacker)
    {
        quackers.add(quacker);
    }

    /**
     * Flock也是Quackable,所以也具备quack()这个方法,此方法会对整个集合产生作用,
     * 遍历ArrayList上的每一个元素上的quack()方法
     */
    @Override
    public void quack() {
        Iterator<Quackable> iterator = quackers.iterator();
        while (iterator.hasNext())
        {
            Quackable quacker = iterator.next();
            quacker.quack();
        }
    }
}

这次不用一个一个传入节点元素了,直接传入组合到simulate()方法里

import compose.Flock;
import decorator.QuackCounter;
import duck.MallardDuck;
import duck.Quackable;
import duck.RedheadDuck;
import duck.RubberDuck;
import factory.AbstractDuckFactory;
import factory.CountingDuckFactory;
import goose.Goose;
import goose.GooseAdapter;

public class Test
{
    public static void main(String[] args) {
        Test test = new Test();
        AbstractDuckFactory countingDuckFactory = new CountingDuckFactory();
        test.go(countingDuckFactory);
    }

   void go(AbstractDuckFactory countingDuckFactory)
    {
        //每创建一个Quackable,就用一个新的装饰者包装它,我们只统计鸭子的叫声,所以不用装饰鹅
//        Quackable mallardDuck = new QuackCounter(new MallardDuck());
//        Quackable redheadDuck =  new QuackCounter(new RedheadDuck());
//        Quackable rubberDuck =  new QuackCounter(new RubberDuck());

        //现在可以利用工厂来创建鸭子对象了
        Quackable mallardDuck = countingDuckFactory.createMallardDuck();
        Quackable redheadDuck = countingDuckFactory.createRedheadDuck();
        Quackable rubberDuck = countingDuckFactory.createRubberDuck();
        //将鹅包装进适配器,就可以让鹅像鸭子一样,拥有鸭子的行为
        Quackable goose = new GooseAdapter(new Goose());

        //创建组合元素Flock,然后把Quackable集合都放进去
        Flock flock = new Flock();
        flock.add(mallardDuck);
        flock.add(redheadDuck);
        flock.add(rubberDuck);
        flock.add(goose);

        //因为组合元素和节点元素都实现了共同接口,操作组合等同于操作节点元素
        System.out.println("开始测试主群");
        simulate(flock);

        //我们查看鸭子呱呱叫了几次
        System.out.println("鸭子总共叫了" + QuackCounter.getCount()+"次");
    }

    /**
     * 这里没有任何变动,被装饰和被适配的对象,还是Quackable类
     * @param quackable
     */
    void simulate(Quackable quackable)
    {
        quackable.quack();
    }
}

完成了同样的效果 ,在组合对象里的遍历,我们也用到了迭代器模式

开始测试主群
呱呱呱
呱呱呱
吱吱吱
咯咯咯
鸭子总共叫了3次

总结:我们做了什么?

有一只鹅出现了,它希望自己像一个Quackable,所以我们利用适配器模式,将鹅是配成Quackable类型,现在你就可以利用鹅的适配器quack()方法让鹅咯咯咯叫

然后呱呱叫学者决定要计算呱呱叫的次数,多以我们用装饰者模式,添加了一个名为QuackCounting的装饰者。它用来追踪quack()被调用的次数。并将调用委托给他所装饰的对象

但是呱呱叫学家担心它们忘了加上Quackcounter装饰者。所以我们使用抽象工厂模式创建鸭子,放他们需要鸭子,就直接跟工厂要,工厂就会给他们装饰过的鸭子。

又是鸭子又是鹅,我们有管理上的困扰,所以我们需要组合模式,将许多quackable集成一个群,我们创建一个组合对象,他也实现了元素对象的接口quackable,使得用户操作它就像和操作元素一样。所以现在呱呱叫学家可以直接操作一个集合了,当需要遍历的时候,用迭代器模式就可以了

发布了138 篇原创文章 · 获赞 34 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/bbj12345678/article/details/105289839
今日推荐