设计模式详解(二)

七:桥接模式(bridge)

1.问题引出:商城系统中很多商品分类,以电脑为例:台式机,笔记本,平板电脑等,我们可以通过下图描述其关系

通过上面的图会发现很多问题:

  • 如果要增加一个新的电脑类型需要再增加各个品牌对应的类
  • 如果要增加一个新的品牌,也要增加各个电脑类型的类
  • 违反单一职责原则:一个类即表示品牌又表示是那种类型的电脑

2.场景分析

商城系统中常见的商品分类,以电脑为类,如何良好的处理商品分类销售的问题?这个场景中有两个变化的维度:电脑类型、电脑品牌。

3.桥接模式核心要点:处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立 的继承结构,使各个维度可以独立的扩展在抽象层建立关联。

4.案例:

/**
 * 品牌接口
 */
public interface Brand {
	void sale();
}

/**
 * 联想品牌
 */
class Lenovo implements Brand{
	@Override
	public void sale() {
		System.out.println("联想电脑......");
	}
}

/**
 * 戴尔品牌
 */
class Dell implements Brand{
	@Override
	public void sale() {
		System.out.println("戴尔电脑......");
	}
}
/**
 * 电脑抽象类,表示电脑类型
 */
public abstract class Computer {
	//绑定品牌
	protected Brand brand;
	public Computer(Brand brand) {
		super();
		this.brand = brand;
	}
	
	public void sale(){
		brand.sale();
	}
}

/**
 * 台式机
 */
class DeskTop extends Computer{
	public DeskTop(Brand brand) {
		super(brand);
	}
	@Override
	public void sale() {
		super.sale();
		System.out.println("销售台式机........");
	}
}

/**
 * 笔记本
 */
class LapTop extends Computer{
	public LapTop(Brand brand) {
		super(brand);
	}
	@Override
	public void sale() {
		super.sale();
		System.out.println("销售笔记本.........");
	}
}
public class Client {
	public static void main(String[] args) {
		//销售联想笔记本,组合代理继承关系
		Computer lapTop = new LapTop(new Lenovo());
		lapTop.sale();
		//新加品牌只需要增加一个Brand的实现类
		//新加平板只需要加一个Computer的子类
		Computer pinban = new Pinban(new Shenzhou());
		pinban.sale();
		//不同组合
		DeskTop deskTop = new DeskTop(new Shenzhou());
		deskTop.sale();
	}
}
class Shenzhou implements Brand{
	@Override
	public void sale() {
		System.out.println("神州笔记本........");
	}
}
class Pinban extends Computer{
	public Pinban(Brand brand) {
		super(brand);
	}
	@Override
	public void sale() {
		super.sale();
		System.out.println("销售平板电脑......");
	}
	
}

这样实现各个类实现单一原则,增加电脑时,只需要添加对应的品牌就电脑类型的实现即可。

5.桥接模式总结:

  • 桥接模式可以取代多层继承的方案。 多层继承违背了单一职责原则, 复用性较差,类的个数也非常多。桥接模式可以极大的减少子类的个 数,从而降低管理和维护的成本。
  • 桥接模式极大的提高了系统可扩展性,在两个变化维度中任意扩展一 个维度,都不需要修改原有的系统,符合开闭原则。

6.应用场景:划分维度两个及以上就适合用桥接模式,比如电脑有品牌,类型等

  • 银行日志管理:格式分类:操作日志、交易日志、异常日志,距离分类:本地记录日志、异地记录日志
  • 人力资源系统中的奖金计算模块:奖金分类:个人奖金、团体奖金、激励奖金。部门分类:人事部门、销售部门、研发部门。
  • OA系统中的消息处理:业务类型:普通消息、加急消息、特急消息。发送消息方式:系统内消息、手机短信、邮件

八:组合模式(composite)

1.组合模式:把部分和整体的关系用树形结构来表示,从而使客户端可以使用统一的方式处理部分对 象和整体对象。

2.组合模式核心

  • 抽象构件(Component)角色: 定义了叶子和容器构件的共同点
  • 叶子(Leaf)构件角色:无子节点
  • 容器(Composite)构件角色: 有容器特征,可以包含子节点

3.组合模式工作流程分析:

  • 组合模式为处理树形结构提供了完美的解决方案,描述了如何将容器和叶子进行递归组 合,使得用户在使用时可以一致性的对待容器和叶子。
  • 当容器对象的指定方法被调用时,将遍历整个树形结构,寻找也包含这个方法的成员, 并调用执行。其中,使用了递归调用的机制对整个结构进行处理。

4.案例

//抽象构建
public interface AbstractFile {
	void killVirus();
}

//图片文件(叶子构建)
class ImageFile implements AbstractFile{
	@Override
	public void killVirus() {
		System.out.println("图片文件删除了........");
	}
}

//文本文件(叶子构建)
class TxtFile implements AbstractFile{
	@Override
	public void killVirus() {
		System.out.println("文本文件删除了........");
	}
}

//视频文件(叶子构建)
class VideoFile implements AbstractFile{
	@Override
	public void killVirus() {
		System.out.println("视频文件删除了........");
	}
}

//文件夹(容器构建)
class Folder implements AbstractFile{
	
	//存储文件夹下的文件
	private List<AbstractFile> fileList=new ArrayList<AbstractFile>();
	
	//添加文件
	public void add(AbstractFile abstractFile){
		fileList.add(abstractFile);
	}
	
	//删除文件
	public void remove(AbstractFile abstractFile){
		fileList.remove(abstractFile);
	}
	
	//获取文件
	public AbstractFile get(int index){
		return fileList.get(index);
	} 
	
	@Override
	public void killVirus() {
		System.out.println("文件夹杀毒开始.......");
		for (AbstractFile abstractFile : fileList) {
			//递归
			abstractFile.killVirus();
		}
	}
}
public class Client {
	
	public static void main(String[] args) {
		
		//创建文件
		AbstractFile imageFile = new ImageFile();
		AbstractFile txtFile = new TxtFile();
		AbstractFile videoFile = new VideoFile();
		
		//创建文件夹
		Folder folder = new Folder();
		folder.add(imageFile);
		folder.add(txtFile);
		folder.add(videoFile);
		
		//创建子文件夹
		Folder folder2 = new Folder();
		AbstractFile imageFile2 = new ImageFile();
		folder2.add(imageFile2);
		
		folder.add(folder2);
		
		//杀毒文件夹,递归调用子文件及子文件夹的killVirus()方法
		folder.killVirus();
		
	}

}

树状结构都是用于组合模式

5.适用场景:

  • 操作系统的资源管理器
  • GUI中的容器层次图
  • XML文件解析
  • OA系统中,组织结构的处理

九:装饰模式(decorator)

1.作用:

  • 动态为一个对象增加新的功能
  • 装饰模式是一种用来代替继承的技术,无需通过继承增加子类就能扩展对象的新功能,适用对象关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀。

2.案例:

/**
 * 抽象接口
 */
public interface ICar {
	void move();
}

//被装饰对象
class Car implements ICar{

	@Override
	public void move() {
		System.out.println("陆地上跑.......");
	}
}

//装饰器
class SuperCar implements ICar{
	public ICar car;
	public SuperCar(ICar car) {
		super();
		this.car = car;
	}

	@Override
	public void move() {
		car.move();
	}
}

//具体装饰器,会飞的车
class FlyCar extends SuperCar{
	public FlyCar(ICar car) {
		super(car);
	}
	@Override
	public void move() {
		super.move();
		fly();
	}
	public void fly() {
		System.out.println("天上飞......");
	}
	
}
//具体装饰器,会游泳的车
class WaterCar extends SuperCar{
	public WaterCar(ICar car) {
		super(car);
	}
	@Override
	public void move() {
		super.move();
		water();
	}
	public void water() {
		System.out.println("水上游......");
	}
}
//具体装饰器,人工智能的车
class AICar extends SuperCar{
	public AICar(ICar car) {
		super(car);
	}
	@Override
	public void move() {
		super.move();
		ai();
	}
	public void ai() {
		System.out.println("自动跑......");
	}
}
public class Client {
	public static void main(String[] args) {
		//一般车
		Car car = new Car();
		car.move();
		//添加新的功能
		FlyCar flyCar = new FlyCar(car);
		flyCar.move();
		//添加复合的功能
		WaterCar waterCar = new WaterCar(flyCar);
		waterCar.move();
		//添加复合的功能
		AICar aiCar = new AICar(waterCar);
		aiCar.move();
	}
}

功能可以自由组合

3.总结:

  • 装饰模式(Decorator)也叫包装器模式(Wrapper)
  • 装饰模式降低系统的耦合度,可以动态的增加或删除对象的职责,并 使得需要装饰的具体构建类和具体装饰类可以独立变化,以便增加新 的具体构建类和具体装饰类。

4.优点:

  • 扩展对象功能,比继承灵活,不会导致类个数急剧增加
  • 可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更 加强大的对象
  • 具体构建类和具体装饰类可以独立变化,用户可以根据需要自己增加 新的具体构件子类和具体装饰子类。

5.缺点:

  • 产生很多小对象。大量小对象占据内存,一定程度上影响性能。
  • 装饰模式易于出错,调试排查比较麻烦。

6.装饰模式和桥接模式的区别:两个模式都是为了解决过多子类对象问题。但他们的诱因不一样。桥接模式是对象自身现有机制沿着多个维度变化,是既有部分不稳定。装饰模式是为了增加新的功能。

十:外观模式(facade)

1.迪米特法则(最少知识原则):一个软件实体应当尽可能少的与其他实体发生相互作用。

例如喝茶,尽可能的减少与茶具,水,茶叶关联,而是直接调用“茶馆服务员”就干完所有的事,外观模式就是解决这种问题的。

2.外观模式核心:为子系统提供统一的入口。封装子系统的复杂性,便于客户端调用。

3.案例:略

4.应用场景:JDBC封装后的,commons提供的DBUtils类, Hibernate提供的工具类、Spring JDBC工具类等

十一:享元模式(FlyWeight)

1.使用场景:内存属于稀缺资源,不要随便浪费。如果有很多个完全相同或相似的 对象,我们可以通过享元模式,节省内存。

2.核心:

  • 享元模式以共享的方式高效地支持大量细粒度对象的重用。
  • 享元对象能做到共享的关键是区分了内部状态和外部状态。内部状态:可以共享,不会随环境变化而改变,外部状态:不可以共享,会随环境变化而改变

3.案例:围棋(棋子颜色为内部对象可共享,棋子位置外部对象,不可共享)

//享元类,棋子颜色,内部状态,可共享
public interface ChessFlyWeight {
	//获取棋子颜色
	String getColor();
	//显示棋子
	void display(Coordinate coordinate);
}

//具体享元类对象
class ConcreteChess implements ChessFlyWeight{
	private String color;
	
	public ConcreteChess(String color) {
		super();
		this.color = color;
	}
	@Override
	public String getColor() {
		return color;
	}
	@Override
	public void display(Coordinate coordinate) {
		System.out.println(color+"\t"+coordinate.getX()+"\t"+coordinate.getY());
	}
}
//棋子地址,外部状态,不可共享
public class Coordinate {
	private int x,y;
	//全参构造,get,set
}
//享元工厂,创建享元类
public class ChessFlyWeightFactory {
	
	//享元池对象
	public static Map<String, ChessFlyWeight> map=new HashMap<String, ChessFlyWeight>();
	
	//获取棋子
	public static ChessFlyWeight getChessFlyWeight(String color) {
		if (map.containsKey(color)) {
			return map.get(color);
		}
		ChessFlyWeight chessFlyWeight = new ConcreteChess(color);
		map.put(color, chessFlyWeight);
		return chessFlyWeight;
	}
	
	public static void main(String[] args) {
		ChessFlyWeight black1 = ChessFlyWeightFactory.getChessFlyWeight("black");
		ChessFlyWeight black2 = ChessFlyWeightFactory.getChessFlyWeight("black");
		System.out.println(black1);//对象相同
		System.out.println(black2);
		
		//内部对象相同,外部对象不相同
		black1.display(new Coordinate(10, 10));//black	10	10
		black2.display(new Coordinate(20,100));//black	20	100
	}
	
}

4.优点:

  • 极大减少内存中对象的数量
  • 相同或相似对象内存中只存一份,极大的节约资源,提高系统性能
  • 外部状态相对独立,不影响内部状态

5.缺点:

  • 模式较复杂,使程序逻辑复杂化
  • 为了节省内存,共享了内部状态,分离出外部状态,而读取外部状态 使运行时间变长。用时间换取了空间。

十二:责任链模式(chain of responsibility)

1.定义:能够处理同一类请求的对象连成一条链,所提交的请求沿着链传递,链上的对象逐个判断是否有能力处理该请求,如果能则处理,否则传递给链上的下一个对象。

2.案例:公司请假为例,层层审批

//假条,封装请假基本信息
public class LaveRequest {
	private String name;//请假人名称
	private int days;//请假天数
	private String reason;//请假原因
	//全参构造,get,set
}
//抽象类,描述各级领导
public abstract class Leader {
	protected String name;//领导名称
	protected Leader nextLeader;//上级领导
	
	//处理请求
	public abstract void handlerRequest(LaveRequest request); 
	
	public Leader(String name, Leader nextLeader) {
		super();
		this.name = name;
		this.nextLeader = nextLeader;
	}
	public void setNextLeader(Leader nextLeader) {
		this.nextLeader = nextLeader;
	}
}

//主任
class Director extends Leader{
	public Director(String name, Leader nextLeader) {
		super(name, nextLeader);
	}
	@Override
	public void handlerRequest(LaveRequest request) {
		if (request.getDays()<3) {
			System.out.println("主任允许啦......");
			return;
		}
		nextLeader.handlerRequest(request);
	}
}
//经理
class Manager extends Leader{
	public Manager(String name, Leader nextLeader) {
		super(name, nextLeader);
	}
	@Override
	public void handlerRequest(LaveRequest request) {
		if (request.getDays()<10) {
			System.out.println("经理允许啦......");
			return;
		}
		nextLeader.handlerRequest(request);
	}
}
//总经理
class GeneralMamager extends Leader{
	public GeneralMamager(String name, Leader nextLeader) {
		super(name, nextLeader);
	}
	@Override
	public void handlerRequest(LaveRequest request) {
		if (request.getDays()<30) {
			System.out.println("总经理允许啦......");
			return;
		}
		System.out.println("不同意.......");
	}
}
public class Client {
	public static void main(String[] args) {
		LaveRequest laveRequest = new LaveRequest("张三", 1, "就是想请假");
		Leader generalMamager = new GeneralMamager("李四", null);
		Leader manager = new Manager("王五", generalMamager);
		Leader director = new Director("赵六",manager);
		director.handlerRequest(laveRequest);
		//如果想要添加其他链的节点,只需要新加一个Leader实现类,加入到链中即可
	}
}

由于责任链的创建完全在客户端,因此新增新的具体处理者对原有类库没有任何影响,只需添加新的类,然后在客户端调用时添加即可。 符合开闭原则。

3.应用场景:

  • Java中,异常机制就是一种责任链模式。一个try可以对应多个catch, 当第一个catch不匹配类型,则自动跳到第二个catch.
  • Javascript语言中,事件的冒泡和捕获机制。Java语言中,事件的处理 采用观察者模式。
  • Servlet开发中,过滤器的链式处理
  • Struts2中,拦截器的调用也是典型的责任链模式

十三:迭代器模式(iterator)

提供一种可以遍历聚合对象的方式。又称为:游标cursor模式 ,聚合对象:存储数据 ,迭代器:遍历数据

案例:

//迭代器接口
public interface MyIterator {
	void first();//将游标指向第一个
	void next();//将游标指向下一个
	boolean hasNext();//判断是否有下一个元素
	Object get();//获取当前对象
}
//自定义聚合类
public class ConcreteMyAggregqte {
	private List<String> list=new ArrayList<String>();
	public void add(String s) {
		list.add(s);
	}
	public void remove(String s) {
		list.remove(s);
	}
	public List<String> getList() {
		return list;
	}
	public void setList(List<String> list) {
		this.list = list;
	}
	
	public MyIterator iterator() {
		return new Iterator();
	}
	
	private class Iterator implements MyIterator{
		//游标
		private int cursor=0;
		@Override
		public void first() {
			cursor=0;
		}
		@Override
		public void next() {
			if (cursor<list.size()-1) {
				++cursor;
			}
		}
		@Override
		public boolean hasNext() {
			return cursor<=list.size()-1;
		}
		@Override
		public Object get() {
			String string = list.get(cursor);
			cursor++;
			return string;
		}
	}
	
	public static void main(String[] args) {
		ConcreteMyAggregqte concreteMyAggregqte = new ConcreteMyAggregqte();
		concreteMyAggregqte.add("aa");
		concreteMyAggregqte.add("bb");
		concreteMyAggregqte.add("cc");
		
		MyIterator iterator = concreteMyAggregqte.iterator();
		while (iterator.hasNext()) {
			System.out.println(iterator.get());
		}
	}

}

十四:中介者模式(mediator)

1.特点:如果一个系统的对象之间关系呈现网状结构,对象之间呈现大量的多对多关系,将导致关系特别复杂,这些对象称为“同事对象”,引入中介者对象,使各个对象跟中介者打交道,将复杂的网状结构转化为星状结构。

2.案例:总经理与各部门之间的关系,总经理就是中介者

/**
 * 中介者接口
 */
public interface Mediator {
	//部门注册
	void register(String dname,Department department);
	//指定部门干活
	void command(String dname);
}

//总经理
class Pre implements Mediator{
	private Map<String, Department> map=new HashMap<String, Department>();
	@Override
	public void register(String dname, Department department) {
		map.put(dname, department);
	}
	@Override
	public void command(String dname) {
		Department department = map.get(dname);
		department.selfAction();
	}
}
/**
 * 部门接口
 */
public interface Department {
	//干自己部门的活
	void selfAction();
	//让总经理协调干活
	void outAction();
}

//研发部
class Dev implements Department{
	private Mediator mediator;
	public Dev(Mediator mediator) {
		super();
		this.mediator = mediator;
		mediator.register("Dev", this);
	}
	@Override
	public void selfAction() {
		System.out.println("研发部开始研发啦........");
	}
	@Override
	public void outAction() {
		System.out.println("研发部汇报工作........");
	}
}

//财务部
class Fin implements Department{
	private Mediator mediator;
	public Fin(Mediator mediator) {
		super();
		this.mediator = mediator;
		mediator.register("Fin", this);
	}
	@Override
	public void selfAction() {
		System.out.println("财务部开始数钱啦........");
	}
	@Override
	public void outAction() {
		System.out.println("财务部汇报工作........");
	}
}

//财务部
class Mar implements Department{
	private Mediator mediator;
	public Mar(Mediator mediator) {
		super();
		this.mediator = mediator;
		mediator.register("Mar", this);
	}
	@Override
	public void selfAction() {
		System.out.println("市场部开始拓展市场啦........");
	}
	@Override
	public void outAction() {
		System.out.println("市场部汇报工作,需要资金支持........");
		mediator.command("Fin");//财务部支持
	}
}
public class Client {
	public static void main(String[] args) {
		Mediator pre = new Pre();
		Department dev = new Dev(pre);
		Department fin = new Fin(pre);
		Department mar = new Mar(pre);
		mar.selfAction();
		mar.outAction();//经过总经理协调,财务部开始数钱啦
	}
}

3.中介者模式的本质:解耦多个同事对象之间的交互关系。每个对象都持有中介者对象的引 用,只跟中介者对象打交道。我们通过中介者对象统一管理这些交互 关系

十五:命令模式(command)

1.介绍:将一个请求(命令相当于一个请求)封装为一个对象,从而使我们可以用不同的请求对客户进行参数化,对请求排队或记录日志,以及支持可撤销操作,也称为“动作Action模式,事务transaction模式”

2.案例

//命令执行者
public class Recelver {
	public void action(){
		System.out.println("action.......");
	}
}
//命令接口
public interface Command {
	//执行方法
	void execute();
}

//命令实现
class ConcreteCommand implements Command{
	private Recelver recelver;
	public ConcreteCommand(Recelver recelver) {
		super();
		this.recelver = recelver;
	}
	@Override
	public void execute() {
		System.out.println("命令执行前......");
		recelver.action();
		System.out.println("命令执行后......");
	}
}
//命令发起者
public class Invaker {
	//一条命令,多条通过容器
	private Command command;
	public Invaker(Command command) {
		super();
		this.command = command;
	}
	public void call(){
		command.execute();
	}
	
	public static void main(String[] args) {
		//创建命令
		Recelver recelver = new Recelver();
		ConcreteCommand concreteCommand = new ConcreteCommand(recelver);
		Invaker invaker = new Invaker(concreteCommand);
		invaker.call();
	}
}

3.应用场景:

  • 数据库事务机制的底层实现
  • Struts2中,action的整个调用过程中就有命令模式。
  • 命令的撤销和恢复
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值