五种基于委托(delegation)设计模式的场景+代码分析

笔者在复习哈工大软件构造的设计模式时,对最基本的五种设计模式,展开了探讨

当然,借着编程操作的机会,也练习了一下正则匹配,防御式编程参数检查等一些软构课上教的 (要考的) 内容
下面是五种基本的设计模式试用场景

工厂方法

基本介绍通过工厂类,将客户端与client进行隔离开
场景: 假如我们要为一个游戏写NPC,考虑到NPC以后的种类会很多,所以,应该建议采用工厂方法而不是new,对NPC进行创建

//main
public class CM {
	public static void main(String []args) {
		Person P=PersonFactory.createPeron("M");//由静态工厂方法调用
		System.out.print(P.getName());
		
	}	
}
//NPC
public class Person {
	private final String name;
	public Person(String N) {
		this.name=N;
	}
	public String getName() {
		return name;
	}
}
//NPC的工厂方法:
package Factory;

public class PersonFactory {
	//普通工厂
	public Person CreatPerson(String n) {
		return new Person(n);
	}
	//静态工厂方法
	public static Person createPeron(String n)
	{
		return new Person(n);
	}
}


装饰器模式

场景: 假如我们的游戏需要对NPC进行包装

穿上了防弹衣可以防弹
穿上雨衣可以防雨
穿上了翅膀可以飞

客户需要构造穿着雨衣的鸟人和穿着防弹衣的鸟人

那么可以这样构造

//human 的接口
public interface Human {
	/**
	 * 进行互动的行为
	 */
	public void action();
}
//human的实现基类
public class ConcreteHuman implements Human{

	@Override
	public void action() {
		System.out.println("我是Human");
	}
	
}

/**
 * 装饰类基类
 */
public class BasicDecorator implements Human {
	private final Human h0;

	public BasicDecorator(Human h1) {
		if (h1 == null) {//进行参数的检查
			throw new NullPointerException();//runTimeException
		}
		h0 = h1;
	}

	@Override
	public void action() {
		h0.action();
	}
}

//三种装饰器
public class RaincoatHuman extends BasicDecorator {

	public RaincoatHuman(Human h1) {
		super(h1);
	}

	@Override
	public void action() {
		System.out.println("这人穿了雨衣");
		super.action();
	}

}
public class FlyableHuman extends BasicDecorator {

	public FlyableHuman(Human h1) {
		super(h1);
	}

	@Override
	public void action() {
		System.out.println("这人长了翅膀可以飞");
		super.action();
	}

}
public class BullyProfHuman extends BasicDecorator {

	public BullyProfHuman(Human h1) {
		super(h1);
	}

	@Override
	public void action() {
		System.out.println("这人刀枪不入");
		super.action();
	}
}
//main函数
	public static void main(String[] args) {
		// 穿着雨衣的鸟人
		Human H0 = new RaincoatHuman(new FlyableHuman(new ConcreteHuman()));
		H0.action();
		// 穿着防弹衣的鸟人
		Human H1 = new BullyProfHuman(new FlyableHuman(new ConcreteHuman()));
		H1.action();

	}

输出:

在这里插入图片描述

具体的UML类图

在这里插入图片描述

适配器模式

场景: 假如我们由一个算法的黑盒子,只能够接受浮点数据为输入,以整形数为输出.而我们现在向用户提供的接口是字符串为输入,字符串为输出.这个时候,需要我们利用适配器,对类进行适配.

注意: 我们需要对前置条件的参数进行检查,同时,也需要检查后置条件是否满足.
采取的思路 :前面的前置,利用正则进行检查,抛出UncheckedException.后面的用assert 来进行判定,判定后置条件是否满足.
思考 :一个合法的浮点数,长什么样子?

  1. 不是0003.14这样子的,这样子太丑了
  2. 可以没有小数点,但是如果有小数点,我们需要对后面的数位进行判定

我们写正则的时候,需要体现对思考的check

//假如我们由一个黑盒:
/**
 * 年份久远的黑盒,可以work
 */
public class OldBlackBox {
	public int getAnswer(double d0) {
		System.out.println("计算中...");
		return 888;
	}
}
//我们向用户承诺了一个API接口
public interface APIUserInterface {
	/**
	 * 我们的API 向用户提供的方法
	 * 
	 * @param 以小数形式的字符串,必须为合法的小数,如为非0098.xxx格式(大于一的,前方不能含有0)
	 * @return 字符串形式的整形数888
	 */
	public String APIWork(String input);
}
//现在我们对这个进行适配

public class Adapter implements APIUserInterface {

	@Override
	public String APIWork(String input) {
		// 进行检查前置条件是否合法
		if (!input.matches("([1-9][\\d]*|[0-9])(\\.[\\d]+)?")) //运用正则
        {
			throw new IllegalArgumentException("输入数据:" + input + "不是合法的浮点数");
		}
		System.out.println("你输入了:" + input);
		// Delegate 黑盒进行计算
		OldBlackBox OBB = new OldBlackBox();
		Double d0 = Double.valueOf(input);
		Integer i = OBB.getAnswer(d0);
		String RET = i.toString();
		// 检查后置条件是否合法
		assert RET.equals("888");
		return RET;
	}
}

//用户代码:
	public static void main(String[] args) {
		APIUserInterface APIU = new Adapter();
		System.out.println(APIU.APIWork("996"));
		System.out.println("--------------");
		System.out.println(APIU.APIWork("18.47"));
		System.out.println("--------------");
		System.out.println(APIU.APIWork("18..47"));

	}

类图

输出:
在这里插入图片描述

迭代器模式

场景: 假如我们向用户提供一个List的抽象,用户可以从头进行顺序遍历到尾部.

List需要满足其类型是Number的子类

//直接写了一个数组

public class UniqueList<L extends Number> implements Iterable {
	private final List<L> L0;

	public UniqueList() {
		L0 = new ArrayList<>();
	}

	public void add(L ll) {
		L0.add(ll);
	}

	// 按照王老师的做法,这里可以直接在类里面写迭代器,这样,就可以直接访问类里面的信息
	private class UniqueIterator<L> implements Iterator<Object> {
		private final List<L> myL = new ArrayList(L0);

		private int current = 0;

		@Override
		public boolean hasNext() {
			return current < myL.size();
		}

		@Override
		public Object next() {
			if (!hasNext()) {
				throw new ArrayIndexOutOfBoundsException("你访问第" + (1 + current) + "个(1~n)元素,导致数组越界");
			}
			L lo = myL.get(current);
			current++;
			return lo;
		}
		@Override
		public void remove() {
			throw new UnsupportedOperationException("删除元素并没有得到支持");
		}
	}
	@Override
	public Iterator<L> iterator() {
		return new UniqueIterator();
	}

}

//客户端
public static void main(String[] args) {
		UniqueList<Integer> UL = new UniqueList<Integer>();
		UL.add(5);
		UL.add(4);
		UL.add(3);
		UL.add(2);
		UL.add(1);
		Iterator<Integer> uli = UL.iterator();
		while (uli.hasNext()) {
			System.out.println(uli.next());
		}
		uli.next();//试图访问越界
    

结果:
在这里插入图片描述

访问者模式

场景:
假如我们已经写好了一个数组的类.考虑到未来的扩展,我们留存了一个accept的方法来允许visitor对其进行访问

现在目标逐渐明确,

1.用一个visitor来进行访问数组,求平均值

2.用一个visitor来访问数组,求总和

3,用一个visitor来访问数组,求向量的模长

默认数组是int类型的

//我们的数组接口
public interface ListInterface0 extends Iterable {

	/**
	 * 获取idx位置的元素
	 * 
	 * @param idx 合法的位置
	 * @return idx位置的元素
	 */
	public int getElement(int idx);

	/**
	 * 添加元素
	 * 
	 * @param val 添加的元素
	 */
	public void addElement(int val);

	/**
	 * 接受访问者v0的扩展
	 * 
	 * @param v0 访问者
	 */
	public void accept(Visitor v0);
}

//我们的访问者接口
public interface Visitor {
	/**
	 * 对列表进行访问
	 * 
	 * @param L0
	 */
	public void visit(ListInterface0 L0);
}

数组接口的实现代码

public class List2 implements ListInterface0 {
	private final List<Integer> L0 = new ArrayList<>();

	@Override
	public int getElement(int idx) {
		return L0.get(idx);
	}

	@Override
	public void addElement(int val) {
		L0.add(val);

	}

	// 按照王老师的做法,这里可以直接在类里面写迭代器,这样,就可以直接访问类里面的信息
	private class UniqueIterator<L> implements Iterator<Object> {
		private final List<L> myL = new ArrayList(L0);

		private int current = 0;

		@Override
		public boolean hasNext() {
			return current < myL.size();
		}

		@Override
		public Object next() {
			if (!hasNext()) {
				throw new ArrayIndexOutOfBoundsException("你访问第" + (1 + current) + "个(1~n)元素,导致数组越界");
			}
			L lo = myL.get(current);
			current++;
			return lo;
		}

		@Override
		public void remove() {
			throw new UnsupportedOperationException("删除元素并没有得到支持");
		}
	}

	@Override
	public Iterator<Integer> iterator() {
		return new UniqueIterator();
	}

	@Override
	public void accept(Visitor v0) {
		v0.visit(this);
	}

}

三种访问者的实现代码:

//平均值访问者
public class MeanVisitor implements Visitor {

	@Override
	public void visit(ListInterface0 L0) {
		int sum = 0;
		int i = 0;
		Iterator<Integer> ii = L0.iterator();
		while (ii.hasNext()) {
			sum += ii.next();
			i++;
		}
		System.out.println("平均值为:" + (double) ((double) sum / i));
	}

}
//总和访问者

public class SumVisitor implements Visitor {

	@Override
	public void visit(ListInterface0 L0) {
		int sum = 0;
		int i = 0;
		Iterator<Integer> ii = L0.iterator();
		while (ii.hasNext()) {
			sum += ii.next();
			i++;
		}
		System.out.println("总和为:" + (sum));
	}

}
//向量长度访问者

public class VecLength implements Visitor {

	@Override
	public void visit(ListInterface0 L0) {
		int sum = 0;
		Iterator<Integer> ii = L0.iterator();
		while (ii.hasNext()) {
			int t = ii.next();
			sum += (t * t);
		}
		System.out.println("向量长度为:" + (sum));
	}

}


主函数

public static void main(String[] args) {
		ListInterface0 L2 = new List2();
		L2.addElement(5);
		L2.addElement(4);
		L2.addElement(3);
		L2.addElement(2);
		L2.addElement(2);
		L2.accept(new MeanVisitor());
		L2.accept(new SumVisitor());
		L2.accept(new VecLength());
	}

输出:

在这里插入图片描述

UML图:
在这里插入图片描述

可以发现,访问者与被访问者互相依赖

小结

根据王忠杰老师所言:这五种设计模式,均是对委托(两颗继承树)的变形与拓展.
在动手操作之后,发现老师说的,还真是蛮有道理的,脑海里对基本的设计模式的UML图有一个基本的映像,动手操作起来,还真是不难的.

以上便是这篇博客的全部内容.
谢谢.

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值