设计模式入门——复合模式(携手合作)

模式通常会被一起使用,并被组合在同一个设计解决方案中。

复合模式在一个解决方案中结合两个或多个模式,以解决一般或者重复发生的问题。

一、 首先我们想建立一个鸭子模拟器

1. 首先我们创建一个Quackable接口:

// 接口
public interface Quackable {
	public void quack();
}

2. 其次我们再实现某些鸭子:

// 标准绿头鸭
public class MallardDuck implements Quackable {
	public void quack() {
		System.out.println("MallarDuck Quack");
	}
}
// 红头鸭
public class RedheadDuck implements Quackable {
	public void quack() {
		System.out.println("RedheadDuck Quack");
	}
}
// 鸭鸣器
public class DuckCall implements Quackable {
	public void quack() {
		System.out.println("DuckCall Kwak");
	}
}
// 橡皮鸭
public class RubberDuck implements Quackable {
	public void quack() {
		System.out.println("RubberDuck Squeak");
	}
}

3. 创建鸭子模拟器

// 模拟器
public class DuckSimulator {

	public static void main(String[] args) {
		DuckSimulator simulator = new DuckSimulator();
		simulator.simulator();
	}

	public void simulator() {
		Quackable mallardDuck = new MallardDuck();
		Quackable redheadDuck = new RedheadDuck();
		Quackable duckCall = new DuckCall();
		Quackable rubberDuck = new RubberDuck();
		
		System.out.println("\nDuck Simulator");
		
		simulator(mallardDuck);
		simulator(redheadDuck);
		simulator(duckCall);
		simulator(rubberDuck);
	}
	
	public void simulator(Quackable duck) {
		duck.quack();
	}

}

4. 运行~

Duck Simulator
MallarDuck Quack
RedheadDuck Quack
DuckCall Kwak
RubberDuck Squeak

二、我们也想让模拟器兼容鹅

1. 创建鹅类

// 鹅来了
public class Goose {
	public void honk() {
		System.out.println("Goose Honk");
	}
}

2. 鹅适配器

但是鹅不能直接扔进去模拟器里,因为类型不同,模拟器只接收实现Quackable接口的类,于是我们需要用到适配器模式将鹅适配成鸭子。

适配器模式:改写接口,将一个接口改成另一个接口以适应目标客户。

// 适配器模式,实现目标接口(Duckable)
public class GooseAdapter implements Quackable {
	Goose goose;

	public GooseAdapter(Goose goose) {
		this.goose = goose;
	}

	// 调用quack()方法后,会被委托到鹅的honk()方法上
	public void quack() {
		goose.honk();
	}

}

3. 在模拟器中使用鹅

// 新的模拟器
public class DuckSimulator {

	public static void main(String[] args) {
		DuckSimulator simulator = new DuckSimulator();
		simulator.simulator();
	}

	public void simulator() {
		Quackable mallardDuck = new MallardDuck();
		Quackable redheadDuck = new RedheadDuck();
		Quackable duckCall = new DuckCall();
		Quackable rubberDuck = new RubberDuck();
		Quackable gooseDuck = new GooseAdapter(new Goose());  // 加了鹅
		
		System.out.println("\nDuck Simulator");
		
		simulator(mallardDuck);
		simulator(redheadDuck);
		simulator(duckCall);
		simulator(rubberDuck);
		simulator(gooseDuck);  // 适配器实现了Quackable()接口,所以可以模拟
	}
	
	public void simulator(Quackable duck) {
		duck.quack();
	}

}

4. 运行结果(鹅也可以用了)

Duck Simulator
MallarDuck Quack
RedheadDuck Quack
DuckCall Kwak
RubberDuck Squeak
Goose Honk

三、有呱呱叫学者想统计一下鸭子叫了几声

1. 创建一个装饰者

不必修改鸭子代码,创建装饰者把鸭子包装起来,给鸭子一些新的行为。

装饰者模式:动态地将责任附加到对象上。想要扩展功能,装饰者提供有别于继承的另一种选择。

// 装饰者
public class QuackCounter implements Quackable{
	Quackable duck;
	static int numberOfQuacks;

	public QuackCounter(Quackable duck) {
		this.duck = duck;
	}
	
    // 给装饰者加入了一个静态方法,以便返回在所有Quackable中发生的叫声次数
	public static int getQuacks() {
		return numberOfQuacks;
	}

	public void quack() { 
		duck.quack();
		numberOfQuacks++;
	}
}

2. 更新此模拟器,以便创建被装饰的鸭子

// 修改模拟器
public class DuckSimulator {

	public static void main(String[] args) {
		DuckSimulator simulator = new DuckSimulator();
		simulator.simulator();
	}

	public void simulator() {
		Quackable mallardDuck = new QuackCounter(new MallardDuck());  //修改的地方在这里,用装设者去包装鸭子
		Quackable redheadDuck = new QuackCounter(new RedheadDuck());
		Quackable duckCall = new QuackCounter(new DuckCall());
		Quackable rubberDuck = new QuackCounter(new RubberDuck());
		Quackable gooseDuck = new GooseAdapter(new Goose());

		System.out.println("\nDuck Simulator");

		simulator(mallardDuck);
		simulator(redheadDuck);
		simulator(duckCall);
		simulator(rubberDuck);
		simulator(gooseDuck);

		System.out.println("The ducks quacked " + QuackCounter.getQuacks() + " times"); // 输出一下
	}

	public void simulator(Quackable duck) {
		duck.quack();
	}

3. 输出

Duck Simulator
MallarDuck Quack
RedheadDuck Quack
DuckCall Kwak
RubberDuck Squeak
Goose Honk
The ducks quacked 4 times

四、为什么要一个一个包装鸭子,万一漏了包装怎么办

我们可以用工厂生产鸭子,确保鸭子的质量

抽象工厂模式:定义一个抽象工厂类,多种类型的工厂继承它,并实例化具体方法,除了继承外另一个优点是可以把一群相关产品集合起来,但如果加入新产品就需要修改接口。

1. 定义一个抽象工厂,子工厂会创建不同的家族

// 抽象工厂
public abstract class AbstractDuckFactory {
	public abstract Quackable createMallardDuck();
	public abstract Quackable createRedheadDuck();
	public abstract Quackable createDuckCall();
	public abstract Quackable createRubberDuck();
}

2. 创建一个没有装饰者的鸭子

// 没有装饰者的鸭子

public class DuckFactory extends AbstractDuckFactory {

    // 这样写,模拟器只知道是一种特定种类的Quackable,并不知道实际是什么。
	public Quackable createMallardDuck() {
		return new MallardDuck();
	}

	public Quackable createRedheadDuck() {
		return new RedheadDuck();
	}

	public Quackable createDuckCall() {
		return new DuckCall();
	}

	public Quackable createRubberDuck() {
		return new RubberDuck();
	}

}

3. 创建具有可以包装鸭子的工厂

public class CountingDuckFactory extends AbstractDuckFactory {

    // 每个方法都会先会被装饰者包装起来
	public Quackable createMallardDuck() {
		return new QuackCounter(new MallardDuck());
	}

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

	public Quackable createDuckCall() {
		return new QuackCounter(new DuckCall());
	}

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

}

4. 要修改模拟器

// 改成用工厂去实例化
public class DuckSimulator {

	public static void main(String[] args) {
		DuckSimulator simulator = new DuckSimulator();
		AbstractDuckFactory duckFactory = new CountingDuckFactory();

		simulator.simulator(duckFactory);
	}

	public void simulator(AbstractDuckFactory duckFactory) {
		Quackable mallardDuck = duckFactory.createMallardDuck();  // 用这种方法创建鸭子了,而不是直接实例化
		Quackable redheadDuck = duckFactory.createRedheadDuck();
		Quackable duckCall = duckFactory.createDuckCall();
		Quackable rubberDuck = duckFactory.createRubberDuck();
		Quackable gooseDuck = new GooseAdapter(new Goose());

		System.out.println("\nDuck Simulator");

		simulator(mallardDuck);
		simulator(redheadDuck);
		simulator(duckCall);
		simulator(rubberDuck);
		simulator(gooseDuck);

		System.out.println("The ducks quacked " + QuackCounter.getQuacks() + " times");
	}

	public void simulator(Quackable duck) {
		duck.quack();
	}

}

5. 运行喽

Duck Simulator
MallarDuck Quack
RedheadDuck Quack
DuckCall Kwak
RubberDuck Squeak
Goose Honk
The ducks quacked 4 times

五、为什么要管个别的鸭子

每次创建一个鸭子,一个一个管理太麻烦了,不如创建一个集合

1. 创建一群鸭子

组合模式允许我们像对待单个对象一样对待对象集合。

组合模式:通常以树形结构实现来表示“整体/部分”的层次结构,将组合和子节点实现统一的接口,从而达到对客户透明的目的。

// 组合和叶节点需要有同样的接口,这里的叶节点是Quackable
public class Flock implements Quackable {
	List<Quackable> quackers = new ArrayList<Quackable>(); // 记录属于这个Flock的Quackable对象
	
	public void add(Quackable quacker) {
		quackers.add(quacker);
	}

	public void quack() {
		Iterator<Quackable> itertor = quackers.iterator();  // 这里是迭代器模式
		while(itertor.hasNext()) {
			Quackable quacker = (Quackable)itertor.next();
			quacker.quack();
		}
	}

}

迭代器模式:提供一个统一的接口可以顺序访问聚合对象中的元素,而不暴露其内部的表示

2. 再修改模拟器

public class DuckSimulator {

	public static void main(String[] args) {
		DuckSimulator simulator = new DuckSimulator();
		AbstractDuckFactory duckFactory = new CountingDuckFactory();

		simulator.simulator(duckFactory);
	}

	public void simulator(AbstractDuckFactory duckFactory) {
		Quackable redheadDuck = duckFactory.createRedheadDuck();
		Quackable duckCall = duckFactory.createDuckCall();
		Quackable rubberDuck = duckFactory.createRubberDuck();
		Quackable gooseDuck = new GooseAdapter(new Goose());

		System.out.println("\nDuck Simulator: With composite = Flocks");
		
		Flock flockOfDucks = new Flock();  // 创建许多个Flock,然后把Quackable塞给它,这是主群

		flockOfDucks.add(redheadDuck);
		flockOfDucks.add(duckCall);
		flockOfDucks.add(rubberDuck);
		flockOfDucks.add(gooseDuck);
		
		Flock flockOfMallards = new Flock(); // 创建一个绿头鸭群
		
		Quackable mallardOne = duckFactory.createMallardDuck();  // 创建小绿头鸭
		Quackable mallardTwo = duckFactory.createMallardDuck();
		Quackable mallardThree = duckFactory.createMallardDuck();
		Quackable mallardFour = duckFactory.createMallardDuck();
		
		flockOfMallards.add(mallardOne);  // 塞给它很多小绿头鸭
		flockOfMallards.add(mallardTwo);
		flockOfMallards.add(mallardThree);
		flockOfMallards.add(mallardFour);
		
		flockOfDucks.add(flockOfMallards);  // 绿头鸭加入主群
		
		System.out.println("\nDuck Simulator: Whole Flock Simulation");
		simulator(flockOfDucks);  // 测试主群
		
		System.out.println("\nDuck Simulator: Mallard Flock Simulation");
		simulator(flockOfMallards);  // 测试绿头鸭群
		
		System.out.println("The ducks quacked " + QuackCounter.getQuacks() + " times");
	}

	public void simulator(Quackable duck) {
		duck.quack();
	}

}

3. 结果

Duck Simulator: With composite = Flocks

Duck Simulator: Whole Flock Simulation
RedheadDuck Quack
DuckCall Kwak
RubberDuck Squeak
Goose Honk
MallarDuck Quack
MallarDuck Quack
MallarDuck Quack
MallarDuck Quack

Duck Simulator: Mallard Flock Simulation
MallarDuck Quack
MallarDuck Quack
MallarDuck Quack
MallarDuck Quack
The ducks quacked 11 times

六、总结

这是一群模式携手合作,不是复合模式。复合模式是指一群模式被结合起来使用,以解决一般性问题。MVC(Model-View-Controller)复合模式。它是由数个模式结合起来而形成的新模式,一再地被用于解决许多设计问题。

并不是说遇到问题就一定要用设计模式来解决,杀鸡焉用牛刀。有时用好的OO设计原则可以解决就可以解决问题。

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值