23种设计模式之结构型模式

前面一篇文章已经介绍了创建型模式,接下来介绍结构型模式.

适配器模式:

适配器模式:将某个类的接口转换成客户端希望的另一个接口表示,主要目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作.其别名为包装器(wrapper).

适配器模式分类:

1.类适配器模式

2.对象适配器模式

3.接口适配器模式

使用场景:

1.国外旅游插座是两口的,需要使用多功能插座进行转换才能使用

2.插座充电是220v,手机需要的是5v手机充电器进行转换

1.类适配器模式

类适配器,最主要的步骤是,适配类转换类(Adapter)要继承被适配类,实现适配接口

package com.hjm.shd.shardingspheredemo.designPatterns.design5.classA;

//被适配的类
public class Voltage220V {
	//输出220V的电压
	public int output220V() {
		int src = 220;
		System.out.println("电压=" + src + "伏");
		return src;
	}
}
package com.hjm.shd.shardingspheredemo.designPatterns.design5.classA;


//适配接口
public interface IVoltage5V {
	public int output5V();
}
package com.hjm.shd.shardingspheredemo.designPatterns.design5.classA;

//适配器类
public class VoltageAdapter extends Voltage220V implements IVoltage5V {

	@Override
	public int output5V() {
		//获取到220V电压
		int srcV = output220V();
		int dstV = srcV / 44 ; //转成 5v
		return dstV;
	}

}
package com.hjm.shd.shardingspheredemo.designPatterns.design5.classA;

public class Phone {

	//充电
	public void charging(IVoltage5V iVoltage5V) {
		if(iVoltage5V.output5V() == 5) {
			System.out.println("电压为5V, 可以充电~~");
		} else if (iVoltage5V.output5V() > 5) {
			System.out.println("电压大于5V, 不能充电~~");
		}
	}
}
package com.hjm.shd.shardingspheredemo.designPatterns.design5.classA;

public class Client {

	public static void main(String[] args) {
		System.out.println(" === 类适配器模式 ====");
		Phone phone = new Phone();
		phone.charging(new VoltageAdapter());
	}

}

类适配器模式优缺点:

1.java是单继承,继承了被适配类,就要求适配类必须为接口,增加了一定的局限性,算是一个缺点.

2.继承了被适配类,可以重写Apader方法,使Apader类更灵活,又算一个优点.

2.对象适配器模式(推举)

对象适配器,是适配器模式中最常用的一种,通过用关联关系来替换继承关系(Apader类)

package com.hjm.shd.shardingspheredemo.designPatterns.design5.classB;

//被适配的类
public class Voltage220V {
	//输出220V的电压,不变
	public int output220V() {
		int src = 220;
		System.out.println("电压=" + src + "伏");
		return src;
	}
}
package com.hjm.shd.shardingspheredemo.designPatterns.design5.classB;


//适配接口
public interface IVoltage5V {
	public int output5V();
}
package com.hjm.shd.shardingspheredemo.designPatterns.design5.classB;

public class Phone {

	//充电
	public void charging(IVoltage5V iVoltage5V) {
		if(iVoltage5V.output5V() == 5) {
			System.out.println("电压为5V, 可以充电~~");
		} else if (iVoltage5V.output5V() > 5) {
			System.out.println("电压大于5V, 不能充电~~");
		}
	}
}
package com.hjm.shd.shardingspheredemo.designPatterns.design5.classB;

//适配器类
public class VoltageAdapter  implements IVoltage5V {

	private Voltage220V voltage220V; // 关联关系-聚合
	
	
	//通过构造器,传入一个 Voltage220V 实例
	public VoltageAdapter(Voltage220V voltage220v) {
		
		this.voltage220V = voltage220v;
	}



	@Override
	public int output5V() {
		
		int dst = 0;
		if(null != voltage220V) {
			int src = voltage220V.output220V();//获取220V 电压
			System.out.println("使用对象适配器,进行适配~~");
			dst = src / 44;
			System.out.println("适配完成,输出的电压为=" + dst);
		}
		
		return dst;
		
	}

}
package com.hjm.shd.shardingspheredemo.designPatterns.design5.classB;

public class Client {

	public static void main(String[] args) {
		System.out.println(" === 对象适配器模式 ====");
		Phone phone = new Phone();
		phone.charging(new VoltageAdapter(new Voltage220V()));
	}

}

对象适配器模式优缺点:

对象适配器模式根据合成复用原则,取消了继承关系,降低成本的同时,更加灵活.

3.接口适配器模式

接口适配器模式又称为缺省适配器模式

当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择的覆盖父类的某些方法来实现需求.

接口适配器适用于一个接口不想使用其所有方法的情况.

package com.hjm.shd.shardingspheredemo.designPatterns.design5.classC;

public interface Interface4 {
	public void m1();
	public void m2();
	public void m3();
	public void m4();
}
package com.hjm.shd.shardingspheredemo.designPatterns.design5.classC;

//在AbsAdapter 我们将 Interface4 的方法进行默认实现
public abstract class AbsAdapter implements Interface4 {

	//默认实现
	public void m1() {

	}

	public void m2() {

	}

	public void m3() {

	}

	public void m4() {

	}
}
package com.hjm.shd.shardingspheredemo.designPatterns.design5.classC;

public class Client {
	public static void main(String[] args) {
		
		AbsAdapter absAdapter = new AbsAdapter() {
			//只需要去覆盖我们 需要使用 接口方法
			@Override
			public void m1() {
				System.out.println("使用了m1的方法");
			}
		};
		
		absAdapter.m1();
	}
}

适配器模式在springMVC框架中的应用:

dispatcherServlet采用了适配器模式:

手动实现springMVC

uml类图:

代码:

package com.hjm.shd.shardingspheredemo.designPatterns.design5.classMVC;

///定义一个Adapter接口 
public interface HandlerAdapter {
	public boolean supports(Object handler);

	public void handle(Object handler);
}

// 多种适配器类

class SimpleHandlerAdapter implements HandlerAdapter {

	public void handle(Object handler) {
		((SimpleController) handler).doSimplerHandler();
	}

	public boolean supports(Object handler) {
		return (handler instanceof SimpleController);
	}

}

class HttpHandlerAdapter implements HandlerAdapter {

	public void handle(Object handler) {
		((HttpController) handler).doHttpHandler();
	}

	public boolean supports(Object handler) {
		return (handler instanceof HttpController);
	}

}

class AnnotationHandlerAdapter implements HandlerAdapter {

	public void handle(Object handler) {
		((AnnotationController) handler).doAnnotationHandler();
	}

	public boolean supports(Object handler) {

		return (handler instanceof AnnotationController);
	}

}
package com.hjm.shd.shardingspheredemo.designPatterns.design5.classMVC;

//多种Controller实现  
public interface Controller {

}

class HttpController implements Controller {
	public void doHttpHandler() {
		System.out.println("http...");
	}
}

class SimpleController implements Controller {
	public void doSimplerHandler() {
		System.out.println("simple...");
	}
}

class AnnotationController implements Controller {
	public void doAnnotationHandler() {
		System.out.println("annotation...");
	}
}
package com.hjm.shd.shardingspheredemo.designPatterns.design5.classMVC;

import java.util.ArrayList;
import java.util.List;

public class DispatchServlet {

	public static List<HandlerAdapter> handlerAdapters = new ArrayList<HandlerAdapter>();

	public DispatchServlet() {
		handlerAdapters.add(new AnnotationHandlerAdapter());
		handlerAdapters.add(new HttpHandlerAdapter());
		handlerAdapters.add(new SimpleHandlerAdapter());
	}

	public void doDispatch() {

		// 此处模拟SpringMVC从request取handler的对象,
		// 适配器可以获取到希望的Controller
		 HttpController controller = new HttpController();
		// AnnotationController controller = new AnnotationController();
		//SimpleController controller = new SimpleController();
		// 得到对应适配器
		HandlerAdapter adapter = getHandler(controller);
		// 通过适配器执行对应的controller对应方法
		adapter.handle(controller);

	}

	public HandlerAdapter getHandler(Controller controller) {
		//遍历:根据得到的controller(handler), 返回对应适配器
		for (HandlerAdapter adapter : this.handlerAdapters) {
			if (adapter.supports(controller)) {
				return adapter;
			}
		}
		return null;
	}

	public static void main(String[] args) {
		new DispatchServlet().doDispatch(); // http...
	}

}

注意事项:

适配器模式的区分,以被适配对象给Adapter的方式来判断是什么适配器.

类适配器:以类给到Adapter,通过继承实现

对象适配器:以对象给到Adapter,通过持有实现.

接口适配器:以接口给到Adapter,通过实现接口来实现.

桥接模式

桥接模式:是将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变.

bridge(桥接)模式基于类的最小设计原则,通过使用封装,聚合,以及继承等行为让不同的类承担不同的职责.把抽象与实现分离开发,从二可以保持各部分的独立性以及应对他们的功能扩展.

使用场景:

1.手机操作问题,假设有多个品牌的手机,每个品牌又分为直立,折叠和旋转类型,在新增一个品牌时,在新增一个类型时不同品牌都需要新增这个类型,这种场景采用桥接模式.

2.JDBC驱动程序

3.银行转账系统

转账分类:网上转账,柜台转账,ATM转账.

转账用户类型:普通用户,银卡用户,金卡用户

4.消息管理

消息类型:即时消息,延时消息

消息分类:手机短信,邮件消息,qq消息.

桥接模式uml类图:

代码:

package com.hjm.shd.shardingspheredemo.designPatterns.design6;

//接口
public interface Brand {
	void open();
	void close();
	void call();
}
package com.hjm.shd.shardingspheredemo.designPatterns.design6;

public class Vivo implements Brand {

	@Override
	public void open() {
		System.out.println(" Vivo手机开机 ");
	}

	@Override
	public void close() {
		System.out.println(" Vivo手机关机 ");
	}

	@Override
	public void call() {
		System.out.println(" Vivo手机打电话 ");
	}

}
package com.hjm.shd.shardingspheredemo.designPatterns.design6;

public class XiaoMi implements Brand {

	@Override
	public void open() {
		System.out.println(" 小米手机开机 ");
	}

	@Override
	public void close() {
		System.out.println(" 小米手机关机 ");
	}

	@Override
	public void call() {
		System.out.println(" 小米手机打电话 ");
	}

}
package com.hjm.shd.shardingspheredemo.designPatterns.design6;

public abstract class Phone {
	
	//组合品牌
	private Brand brand;

	//构造器
	public Phone(Brand brand) {
		super();
		this.brand = brand;
	}
	
	protected void open() {
		this.brand.open();
	}
	protected void close() {
		brand.close();
	}
	protected void call() {
		brand.call();
	}
	
}
package com.hjm.shd.shardingspheredemo.designPatterns.design6;


//折叠式手机类,继承 抽象类 Phone
public class FoldedPhone extends Phone {

	//构造器
	public FoldedPhone(Brand brand) {
		super(brand);
	}
	
	public void open() {
		super.open();
		System.out.println(" 折叠样式手机 ");
	}
	
	public void close() {
		super.close();
		System.out.println(" 折叠样式手机 ");
	}
	
	public void call() {
		super.call();
		System.out.println(" 折叠样式手机 ");
	}
}
package com.hjm.shd.shardingspheredemo.designPatterns.design6;

public class UpRightPhone extends Phone {
	
		//构造器
		public UpRightPhone(Brand brand) {
			super(brand);
		}
		
		public void open() {
			super.open();
			System.out.println(" 直立样式手机 ");
		}
		
		public void close() {
			super.close();
			System.out.println(" 直立样式手机 ");
		}
		
		public void call() {
			super.call();
			System.out.println(" 直立样式手机 ");
		}
}
package com.atguigu.bridge;

public class Client {

	public static void main(String[] args) {
		
		//获取折叠式手机 (样式 + 品牌 )
		
		Phone phone1 = new FoldedPhone(new XiaoMi());
		
		phone1.open();
		phone1.call();
		phone1.close();
		
		System.out.println("=======================");
		
		Phone phone2 = new FoldedPhone(new Vivo());
		
		phone2.open();
		phone2.call();
		phone2.close();
		
		System.out.println("==============");
		
		UpRightPhone phone3 = new UpRightPhone(new XiaoMi());
		
		phone3.open();
		phone3.call();
		phone3.close();
		
		System.out.println("==============");
		
		UpRightPhone phone4 = new UpRightPhone(new Vivo());
		
		phone4.open();
		phone4.call();
		phone4.close();
	}

}

桥接模式在JDBC中的应用:

桥接模式在jdbc driver中来看,driver就是一个接口,下面可以有mysql的driver,oracle的driver,db2的driver,这些可以当做接口实现类.

uml类图:

桥接模式优缺点:

1.实现了抽象和实现部分的分离,从而极大的提供了系统的灵活性.让抽象部分和实现部分独立开来,有助于系统的分层设计,从而产生更好的结构化系统

2.桥接模式替代多层继承方案,可以减少子类的个数,降低系统的管理和维护成本.

3.桥接模式的引入增加了系统的理解和设计难度,由于聚合关联关系的建立在抽象层,要求开发者针对抽象进行设计和编程.

4.桥接模式要正确识别出系统中两个独立变化的维度,因此使用起来有一定的局限性.

装饰者模式:

装饰者模式:动态的将新功能附加到对象上.在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)

使用场景:

1.星巴克咖啡订单项目

咖啡种类分为多种,每个种类还可以添加调料,要求在扩展新的咖啡种类时,具有良好的扩展性,方便维护.客户可以点单品咖啡,也可以点单品咖啡+调料组合.计算订单的费用.

代码:

定义一个喝的基类

package com.hjm.shd.shardingspheredemo.designPatterns.design7;

public abstract class Drink {

	public String des; // 描述
	private float price = 0.0f;
	public String getDes() {
		return des;
	}
	public void setDes(String des) {
		this.des = des;
	}
	public float getPrice() {
		return price;
	}
	public void setPrice(float price) {
		this.price = price;
	}
	
	//计算费用的抽象方法
	//子类来实现
	public abstract float cost();
	
}

定义一个装饰者,继承基类,组合基类,重写计算费用方法.

package com.hjm.shd.shardingspheredemo.designPatterns.design7;

//装饰者
public class Decorator extends Drink {
	private Drink obj;
	
	public Decorator(Drink obj) { //组合
		this.obj = obj;
	}
	
	@Override
	public float cost() {
		// getPrice 自己价格
		return super.getPrice() + obj.cost();
	}
	
	@Override
	public String getDes() {
		// obj.getDes() 输出被装饰者的信息
		return des + " " + getPrice() + " && " + obj.getDes();
	}
	
	

}

定义调味品(即,具体装饰者)继承装饰者

package com.hjm.shd.shardingspheredemo.designPatterns.design7;


//具体的Decorator, 这里就是调味品
public class Chocolate extends Decorator {

	public Chocolate(Drink obj) {
		super(obj);
		setDes(" 巧克力 ");
		setPrice(3.0f); // 调味品 的价格
	}

}
package com.hjm.shd.shardingspheredemo.designPatterns.design7;

public class Milk extends Decorator {

	public Milk(Drink obj) {
		super(obj);
		setDes(" 牛奶 ");
		setPrice(2.0f); 
	}

}
package com.hjm.shd.shardingspheredemo.designPatterns.design7;

public class Soy extends Decorator{

	public Soy(Drink obj) {
		super(obj);
		setDes(" 豆浆  ");
		setPrice(1.5f);
	}

}

定义一个咖啡基类,继承Drink

package com.hjm.shd.shardingspheredemo.designPatterns.design7;

public class Coffee  extends Drink {

	@Override
	public float cost() {
		return super.getPrice();
	}

	
}

其他咖啡类型继承咖啡基类

package com.hjm.shd.shardingspheredemo.designPatterns.design7;

public class DeCaf extends Coffee {

	public DeCaf() {
		setDes(" 无因咖啡 ");
		setPrice(1.0f);
	}
}
package com.hjm.shd.shardingspheredemo.designPatterns.design7;

public class LongBlack extends Coffee {

	public LongBlack() {
		setDes(" longblack ");
		setPrice(5.0f);
	}
}
package com.hjm.shd.shardingspheredemo.designPatterns.design7;

public class ShortBlack extends Coffee{
	
	public ShortBlack() {
		setDes(" shortblack ");
		setPrice(4.0f);
	}
}
package com.hjm.shd.shardingspheredemo.designPatterns.design7;

public class Espresso extends Coffee {
	
	public Espresso() {
		setDes(" 意大利咖啡 ");
		setPrice(6.0f);
	}
}

客户端

package com.hjm.shd.shardingspheredemo.designPatterns.design7;


public class CoffeeBar {

	public static void main(String[] args) {
		// 装饰者模式下的订单:2份巧克力+一份牛奶的LongBlack

		// 1. 点一份 LongBlack
		Drink order = new LongBlack();
		System.out.println("费用1=" + order.cost());
		System.out.println("描述=" + order.getDes());

		// 2. order 加入一份牛奶
		order = new Milk(order);

		System.out.println("order 加入一份牛奶 费用 =" + order.cost());
		System.out.println("order 加入一份牛奶 描述 = " + order.getDes());

		// 3. order 加入一份巧克力

		order = new Chocolate(order);

		System.out.println("order 加入一份牛奶 加入一份巧克力  费用 =" + order.cost());
		System.out.println("order 加入一份牛奶 加入一份巧克力 描述 = " + order.getDes());

		// 3. order 加入一份巧克力

		order = new Chocolate(order);

		System.out.println("order 加入一份牛奶 加入2份巧克力   费用 =" + order.cost());
		System.out.println("order 加入一份牛奶 加入2份巧克力 描述 = " + order.getDes());
	
		System.out.println("===========================");
		
		Drink order2 = new DeCaf();
		
		System.out.println("order2 无因咖啡  费用 =" + order2.cost());
		System.out.println("order2 无因咖啡 描述 = " + order2.getDes());
		
		order2 = new Milk(order2);
		
		System.out.println("order2 无因咖啡 加入一份牛奶  费用 =" + order2.cost());
		System.out.println("order2 无因咖啡 加入一份牛奶 描述 = " + order2.getDes());

	
	}

}

装饰者模式在jdk源码分析:

1.InputStream 是抽象类,相当于Drink

2.FileInputStream 是 InputStream的子类,类似于我们前面的LongBlack.

3.FilterInputStream 是 InputStream 子类,类似于Decorator装饰者

4.DataInputStream 是 FilterInputStream 子类,具体的装饰者,类似于前面的milk,soy等

5.FilterInputStream 类有 protected Volatile InputStream in 即包含被装饰者.

由此可见,在jdk的io体系同,就是使用装饰者模式来构建的.

组合模式:

组合模式:又叫做部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以表示"整体-部分"的层级关系,组合模式依据树形结构来组合对象,用来表示部分以及整体层次.组合能让客户以一致的方式处理个别对象以及组合对象.

使用场景:

解决在一个页面中展示学校和学校院系组成需求,一个学校有多个院系,一个院系有多个专业.

组合模式的角色及职责:

component:这是组合模式中对象声明接口,在适当情况下,实现所有类共有的接口默认行为,用于访问和管理component子部件,component可以使抽象类或者接口.

leaf:在组合中标识叶子节点,叶子节点没有子节点.

composite:非叶子节点,用于存储子部件,在component接口中实现子部件的相关操作,比如新增,删除.

组合模式可以解决的问题:

当我们要处理的对象可以生成一颗树形结构,而我们要对树上的节点和叶子进行操作时,它能够提供一致的方式,而不用考虑它是节点还是叶子.

代码:

package com.hjm.shd.shardingspheredemo.designPatterns.design8;

public abstract class OrganizationComponent {

	private String name; // 名字
	private String des; // 说明
	
	protected  void add(OrganizationComponent organizationComponent) {
		//默认实现
		throw new UnsupportedOperationException();
	}
	
	protected  void remove(OrganizationComponent organizationComponent) {
		//默认实现
		throw new UnsupportedOperationException();
	}

	//构造器
	public OrganizationComponent(String name, String des) {
		super();
		this.name = name;
		this.des = des;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getDes() {
		return des;
	}

	public void setDes(String des) {
		this.des = des;
	}
	
	//方法print, 做成抽象的, 子类都需要实现
	protected abstract void print();
	
	
}
package com.hjm.shd.shardingspheredemo.designPatterns.design8;

import java.util.ArrayList;
import java.util.List;

//学校
//University 就是 Composite , 可以管理College
public class University extends OrganizationComponent {

	List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();

	// 构造器
	public University(String name, String des) {
		super(name, des);
	}

	// 重写add
	@Override
	protected void add(OrganizationComponent organizationComponent) {

		organizationComponents.add(organizationComponent);
	}

	// 重写remove
	@Override
	protected void remove(OrganizationComponent organizationComponent) {

		organizationComponents.remove(organizationComponent);
	}

	@Override
	public String getName() {

		return super.getName();
	}

	@Override
	public String getDes() {

		return super.getDes();
	}

	// print方法,就是输出University 包含的学院
	@Override
	protected void print() {
		System.out.println("--------------" + getName() + "--------------");
		//遍历 organizationComponents 
		for (OrganizationComponent organizationComponent : organizationComponents) {
			organizationComponent.print();
		}
	}

}
package com.hjm.shd.shardingspheredemo.designPatterns.design8;

import java.util.ArrayList;
import java.util.List;

//院系
public class College extends OrganizationComponent {

	//List 中 存放的Department
	List<OrganizationComponent> organizationComponents = new ArrayList<OrganizationComponent>();

	// 构造器
	public College(String name, String des) {
		super(name, des);
	}

	// 重写add
	@Override
	protected void add(OrganizationComponent organizationComponent) {

		//  将来实际业务中,Colleage 的 add 和  University add 不一定完全一样
		organizationComponents.add(organizationComponent);
	}

	// 重写remove
	@Override
	protected void remove(OrganizationComponent organizationComponent) {

		organizationComponents.remove(organizationComponent);
	}

	@Override
	public String getName() {

		return super.getName();
	}

	@Override
	public String getDes() {

		return super.getDes();
	}

	// print方法,就是输出University 包含的学院
	@Override
	protected void print() {
		System.out.println("--------------" + getName() + "--------------");
		//遍历 organizationComponents 
		for (OrganizationComponent organizationComponent : organizationComponents) {
			organizationComponent.print();
		}
	}


}
package com.hjm.shd.shardingspheredemo.designPatterns.design8;

//专业
public class Department extends OrganizationComponent {

	//没有集合
	
	public Department(String name, String des) {
		super(name, des);
	}

	
	//add , remove 就不用写了,因为他是叶子节点
	
	@Override
	public String getName() {

		return super.getName();
	}
	
	@Override
	public String getDes() {
		return super.getDes();
	}
	
	@Override
	protected void print() {
		System.out.println(getName());
	}

}
package com.hjm.shd.shardingspheredemo.designPatterns.design8;

public class Client {

	public static void main(String[] args) {
		
		//从大到小创建对象 学校
		OrganizationComponent university = new University("清华大学", " 中国顶级大学 ");
		
		//创建 学院
		OrganizationComponent computerCollege = new College("计算机学院", " 计算机学院 ");
		OrganizationComponent infoEngineercollege = new College("信息工程学院", " 信息工程学院 ");
		
		
		//创建各个学院下面的系(专业)
		computerCollege.add(new Department("软件工程", " 软件工程不错 "));
		computerCollege.add(new Department("网络工程", " 网络工程不错 "));
		computerCollege.add(new Department("计算机科学与技术", " 计算机科学与技术是老牌的专业 "));
		
		//
		infoEngineercollege.add(new Department("通信工程", " 通信工程不好学 "));
		infoEngineercollege.add(new Department("信息工程", " 信息工程好学 "));
		
		//将学院加入到 学校
		university.add(computerCollege);
		university.add(infoEngineercollege);
		
		//university.print();
		infoEngineercollege.print();
	}

}

组合模式在JDK集合源码分析:

uml类图:

1.map就是一个抽象的构建(类似于我们的component)

2.HashMap是一个中间构建(composite),实现/继承了相关方法,put,putall

3.node 是HashMap的静态内部类,类似于leaf叶子节点,这里没有put和putall方法.

static class Node<K,V> implements Map.Entry<K,V>

组合模式的优缺点:

1.简化客户端操作

2.具有较强的扩展性

3.方便创建出复杂的层次结构.

4.需要遍历组织机构,或者处理的对象具有树形结构时,非常适合使用组合模式.

5.要求较高的抽象性,如果节点和叶子有很多的差异的话,不适用于组合模式.

外观模式:

外观模式也叫做过程模式,外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用.

外观模式通过定义一个一致的接口,用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关心这个子系统的内部细节.

使用场景:

影院管理项目:

组建一个家庭影院,需要打开一系列的软件例如:

直接使用遥控器,统筹各种设备开关,开爆米花机,开投影仪,开音响,开DVD,调灯光等,观影结束后,需要关闭各种设备.

外观模式uml类图以及各个角色说明:

1.外观类(Facade):为调用端提供统一的调用接口,外观类知道哪些子系统负责处理请求,从而将调用端的请求代理给适当子系统对象.

2.调用者(client):外观接口的调用者

3.子系统集合:指模块或者子系统,处理facade对象指派的任务,他是功能实际提供者.

代码:

整合各种设备

package com.hjm.shd.shardingspheredemo.designPatterns.design9;

//DVD
public class DVDPlayer {
	
	//使用单例模式, 使用饿汉式
	private static DVDPlayer instance = new DVDPlayer();
	
	public static DVDPlayer getInstanc() {
		return instance;
	}
	
	public void on() {
		System.out.println(" dvd on ");
	}
	public void off() {
		System.out.println(" dvd off ");
	}
	
	public void play() {
		System.out.println(" dvd is playing ");
	}
	
	//....
	public void pause() {
		System.out.println(" dvd pause ..");
	}
}
package com.hjm.shd.shardingspheredemo.designPatterns.design9;

//爆米花机
public class Popcorn {
	
	private static Popcorn instance = new Popcorn();
	
	public static Popcorn getInstance() {
		return instance;
	}
	
	public void on() {
		System.out.println(" popcorn on ");
	}
	
	public void off() {
		System.out.println(" popcorn off ");
	}
	
	public void pop() {
		System.out.println(" popcorn is poping  ");
	}
}
package com.hjm.shd.shardingspheredemo.designPatterns.design9;

//放映机
public class Projector {

	private static Projector instance = new Projector();
	
	public static Projector getInstance() {
		return instance;
	}
	
	public void on() {
		System.out.println(" Projector on ");
	}
	
	public void off() {
		System.out.println(" Projector ff ");
	}
	
	public void focus() {
		System.out.println(" Projector is Projector  ");
	}
	
	//...
}
package com.hjm.shd.shardingspheredemo.designPatterns.design9;

//屏幕
public class Screen {

	private static Screen instance = new Screen();
	
	public static Screen getInstance() {
		return instance;
	}
	
	public void up() {
		System.out.println(" Screen up ");
	}
	
	public void down() {
		System.out.println(" Screen down ");
	}
	

}
package com.hjm.shd.shardingspheredemo.designPatterns.design9;

//立体声
public class Stereo {

	private static Stereo instance = new Stereo();
	
	public static Stereo getInstance() {
		return instance;
	}
	
	public void on() {
		System.out.println(" Stereo on ");
	}
	
	public void off() {
		System.out.println(" Screen off ");
	}
	
	public void up() {
		System.out.println(" Screen up.. ");
	}
	
	//...
}
package com.hjm.shd.shardingspheredemo.designPatterns.design9;

//灯光
public class TheaterLight {

	private static TheaterLight instance = new TheaterLight();

	public static TheaterLight getInstance() {
		return instance;
	}

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

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

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

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

家庭观影类

package com.hjm.shd.shardingspheredemo.designPatterns.design9;
//家庭影院
public class HomeTheaterFacade {
	
	//定义各个子系统对象
	private TheaterLight theaterLight;
	private Popcorn popcorn;
	private Stereo stereo;
	private Projector projector;
	private Screen screen;
	private DVDPlayer dVDPlayer;
	
	
	//构造器
	public HomeTheaterFacade() {
		super();
		this.theaterLight = TheaterLight.getInstance();
		this.popcorn = Popcorn.getInstance();
		this.stereo = Stereo.getInstance();
		this.projector = Projector.getInstance();
		this.screen = Screen.getInstance();
		this.dVDPlayer = DVDPlayer.getInstanc();
	}

	//操作分成 4 步
	
	public void ready() {
		popcorn.on();
		popcorn.pop();
		screen.down();
		projector.on();
		stereo.on();
		dVDPlayer.on();
		theaterLight.dim();
	}
	
	public void play() {
		dVDPlayer.play();
	}
	
	public void pause() {
		dVDPlayer.pause();
	}
	
	public void end() {
		popcorn.off();
		theaterLight.bright();
		screen.up();
		projector.off();
		stereo.off();
		dVDPlayer.off();
	}


	
	
	
}

客户端:

package com.hjm.shd.shardingspheredemo.designPatterns.design9;

public class Client {

	public static void main(String[] args) {
		//这里直接调用。。 很麻烦
		HomeTheaterFacade homeTheaterFacade = new HomeTheaterFacade();
		homeTheaterFacade.ready();
		homeTheaterFacade.play();
		
		
		homeTheaterFacade.end();
	}

}

外观模式在mybatis框架中的分析:

1.在ibatis包下的configuration类中有DefaultReflectorFactory, DefaultObjectFactory, DefaultObjectWrapperFactory 这三个实例对象.

2.在调用newMetaObject方法时,通过forObject方法,根据构造器创建不同的对象.从而达到控制不同类的效果

外观模式的优缺点:

1.外观模式对外屏蔽了系统的细节,因此外观模式降低了客户端对子系统使用的复杂性

2.外观模式更好的解耦了客户端和子系统的耦合性,让系统更方便维护

3.当两个模块之间调用比较简单时,不要过度使用外观模式,当系统过于复杂,很难维护和扩展,可以考虑使用外观模式,提供比较清晰简单的接口,来提高复用性.

4.当系统需要分层设计时,可以考虑使用外观模式(facade模式).

享元模式:

享元模式又称蝇量模式,运用共享技术有效地支持大量细粒度的对象.

常用于系统底层开发,解决系统性能问题.例如数据库连接池,里面都是创建好的连接对象.

享元模式重复对象内存浪费问题,当系统中有大量相似对象,需要缓冲池时,不总是创建对象,而是直接从缓冲池中拿,这样可以降低内存,提高效率.

享元模式经典的应用场景就是池技术了,String常量池,数据库连接池,缓冲池等等,都是享元模式的应用

使用场景:

1.线程池,连接池常量池技术

2.展示网站项目需求

小型外包项目,给不同客户做一个类似的网站展示项目,内容相似,但是展示的具体形式不一样.

享元模式uml类图:

1.FlyWeight 是抽象的享元角色,他是产品的抽象类,同事定义出对象的外部状态和内部状态的接口或者实现.

2.ConcreteFlyWeight 是具体的享元角色,是具体的产品类,实现抽象角色定义相关业务.

3.UnSharedConcreteFlyWeight 是不可共享的角色,一般不会出现在享元工厂

4.FlyWeightFactory 享元工厂类,用于构建一个池容器(集合),同时提供从池中获取对象的方法.

内部状态:

指对象共享出来的信息,存储在享元对象内部且不会随环境的改变而改变.

外部状态:

指对象得以依赖的一个标记,是随环境改变而改变的,不可共享的状态.

代码:

package com.hjm.shd.shardingspheredemo.designPatterns.design10;

public abstract class WebSite {

	public abstract void use(User user);//抽象方法
}
package com.hjm.shd.shardingspheredemo.designPatterns.design10;

//具体网站
public class ConcreteWebSite extends WebSite {

	//共享的部分,内部状态
	private String type = ""; //网站发布的形式(类型)

	
	//构造器
	public ConcreteWebSite(String type) {
		
		this.type = type;
	}


	@Override
	public void use(User user) {
		System.out.println("网站的发布形式为:" + type + " 在使用中 .. 使用者是" + user.getName());
	}
	
	
}
package com.hjm.shd.shardingspheredemo.designPatterns.design10;

import java.util.HashMap;

// 网站工厂类,根据需要返回一个网站
public class WebSiteFactory {

	
	//集合, 充当池的作用
	private HashMap<String, ConcreteWebSite> pool = new HashMap<>();
	
	//根据网站的类型,返回一个网站, 如果没有就创建一个网站,并放入到池中,并返回
	public WebSite getWebSiteCategory(String type) {
		if(!pool.containsKey(type)) {
			//就创建一个网站,并放入到池中
			pool.put(type, new ConcreteWebSite(type));
		}
		
		return (WebSite)pool.get(type);
	}
	
	//获取网站分类的总数 (池中有多少个网站类型)
	public int getWebSiteCount() {
		return pool.size();
	}
}
package com.hjm.shd.shardingspheredemo.designPatterns.design10;

public class User {
	
	private String name;

	
	public User(String name) {
		super();
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	
}
package com.hjm.shd.shardingspheredemo.designPatterns.design10;

public class Client {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		// 创建一个工厂类
		WebSiteFactory factory = new WebSiteFactory();

		// 客户要一个以新闻形式发布的网站
		WebSite webSite1 = factory.getWebSiteCategory("新闻");

		
		webSite1.use(new User("tom"));

		// 客户要一个以博客形式发布的网站
		WebSite webSite2 = factory.getWebSiteCategory("博客");

		webSite2.use(new User("jack"));

		// 客户要一个以博客形式发布的网站
		WebSite webSite3 = factory.getWebSiteCategory("博客");

		webSite3.use(new User("smith"));

		// 客户要一个以博客形式发布的网站
		WebSite webSite4 = factory.getWebSiteCategory("博客");

		webSite4.use(new User("king"));
		
		System.out.println("网站的分类共=" + factory.getWebSiteCount());
	}

}

享元模式在JDK源码分析:

package com.hjm.shd.shardingspheredemo.designPatterns.jdk;

public class FlyWeight {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//如果 Integer.valueOf(x) x 在  -128 --- 127 直接,就是使用享元模式返回,如果不在
		//范围类,则仍然 new 
		
		//小结:
		//1. 在valueOf 方法中,先判断值是否在 IntegerCache 中,如果不在,就创建新的Integer(new), 否则,就直接从 缓存池返回
		//2. valueOf 方法,就使用到享元模式
		//3. 如果使用valueOf 方法得到一个Integer 实例,范围在 -128 - 127 ,执行速度比 new 快
		
		
		Integer x = Integer.valueOf(127); // 得到 x实例,类型 Integer
		Integer y = new Integer(127); // 得到 y 实例,类型 Integer
		Integer z = Integer.valueOf(127);//..
		Integer w = new Integer(127);
		
		
		
		System.out.println(x.equals(y)); // 大小,true
		System.out.println(x == y ); //  false
		System.out.println(x == z ); // true
		System.out.println(w == x ); // false
		System.out.println(w == y ); // false
		
		
		Integer x1 = Integer.valueOf(200);
		Integer x2 = Integer.valueOf(200);
		System.out.println("x1==x2" + (x1 == x2)); // false
		
		
		


	}

}

享元模式优缺点:

1.享元模式大大减少了对象的创建,降低了程序内存占用,提高了效率.

2.享元模式提高了系统的复杂度,需要分离出内部状态和外部状态,而且外部状态具有固化的特性,不应该随着内部状态的改变而改变,这是我们使用享元模式需要注意的地方.

3.使用享元模式,注意划分内部状态和外部状态,并且需要一个工厂类加以控制.

代理模式:

代理模式:为一个对象提供一个替身,以控制对这个对象的访问.即通过代理对象访问目标对象,这样做的好处是,可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象功能.

代理模式三种形式:

1.静态代理

2.动态代理(JDK代理,接口代理)

3.Cglib代理(可以在内存动态创建对象,而不需要实现接口,他是属于动态代理范畴的).

静态代理:

静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者继承相同的父类.

uml类图:

代码:

package com.hjm.shd.shardingspheredemo.designPatterns.design11.staticProxy;


//接口
public interface ITeacherDao {
	
	void teach(); // 授课的方法
}
package com.hjm.shd.shardingspheredemo.designPatterns.design11.staticProxy;

public class TeacherDao implements ITeacherDao {

	@Override
	public void teach() {
		System.out.println(" 老师授课中  。。。。。");
	}

}
package com.hjm.shd.shardingspheredemo.designPatterns.design11.staticProxy;

//代理对象,静态代理
public class TeacherDaoProxy implements ITeacherDao{
	
	private ITeacherDao target; // 目标对象,通过接口来聚合
	
	
	//构造器
	public TeacherDaoProxy(ITeacherDao target) {
		this.target = target;
	}



	@Override
	public void teach() {
		System.out.println("开始代理  完成某些操作。。。。。 ");//方法
		target.teach();
		System.out.println("提交。。。。。");//方法
	}

}
package com.hjm.shd.shardingspheredemo.designPatterns.design11.staticProxy;

public class Client {

	public static void main(String[] args) {
		//创建目标对象(被代理对象)
		TeacherDao teacherDao = new TeacherDao();
		
		//创建代理对象, 同时将被代理对象传递给代理对象
		TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);
		
		//通过代理对象,调用到被代理对象的方法
		//即:执行的是代理对象的方法,代理对象再去调用目标对象的方法 
		teacherDaoProxy.teach();
	}

}

静态代理优缺点

1.优点:在不修改目标对象功能的前提下,能通过代理对象对目标功能扩展.

2.缺点:因为代理对象与目标对象实现一样的接口,所以会有很多的代理类,一旦接口增加方法,目标对象与代理对象都需要维护.

动态代理:

1.动态代理,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理

2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象.

3.动态代理:也叫做JDK代理,接口代理.

JDK中生成代理对象的API

代理类所在的包:java.lang.reflect.Proxy

jdk实现代理需要使用newProxyInstance方法,该方法需要接收三个参数,完整写法:

static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHander h )

uml类图:

代码:

package com.hjm.shd.shardingspheredemo.designPatterns.design11.dynamicProxy;

//接口
public interface ITeacherDao {

	void teach(); // 授课方法
	void sayHello(String name);
}
package com.hjm.shd.shardingspheredemo.designPatterns.design11.dynamicProxy;

public class TeacherDao implements ITeacherDao {

	@Override
	public void teach() {
		System.out.println(" 老师授课中.... ");
	}

	@Override
	public void sayHello(String name) {
		System.out.println("hello " + name);
	}
	
}
package com.hjm.shd.shardingspheredemo.designPatterns.design11.dynamicProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyFactory {

	//维护一个目标对象 , Object
	private Object target;

	//构造器 , 对target 进行初始化
	public ProxyFactory(Object target) {
		
		this.target = target;
	} 
	
	//给目标对象 生成一个代理对象
	public Object getProxyInstance() {
		
		//说明
		/*
		 *  public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
                                          
            //1. ClassLoader loader : 指定当前目标对象使用的类加载器, 获取加载器的方法固定
            //2. Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型
            //3. InvocationHandler h : 事情处理,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行的目标对象方法作为参数传入
		 */
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
				target.getClass().getInterfaces(), 
				new InvocationHandler() {
					
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						System.out.println("JDK代理开始~~");
						//反射机制调用目标对象的方法
						Object returnVal = method.invoke(target, args);
						System.out.println("JDK代理提交");
						return returnVal;
					}
				}); 
	}
	
	
}
package com.hjm.shd.shardingspheredemo.designPatterns.design11.dynamicProxy;

public class Client {

	public static void main(String[] args) {

		//创建目标对象
		ITeacherDao target = new TeacherDao();
		
		//给目标对象,创建代理对象, 可以转成 ITeacherDao
		ITeacherDao proxyInstance = (ITeacherDao)new ProxyFactory(target).getProxyInstance();
	
		// proxyInstance=class com.sun.proxy.$Proxy0 内存中动态生成了代理对象
		System.out.println("proxyInstance=" + proxyInstance.getClass());
		
		//通过代理对象,调用目标对象的方法
		//proxyInstance.teach();
		
		proxyInstance.sayHello(" tom ");
	}

}

Cglib动态代理:

1.静态代理和动态代理都要求被代理对象实现一个接口,但是cglib代理不需要

2.cglib代理也叫做子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.

3.Cglib动态代理被广泛应用在AOP框架中,例如:SpringAOP,实现方法拦截.

4.在AOP编程中,如何选择代理模式:

        1)目标对象需要实现接口,用JDK代理

        2)目标对象不需要实现接口,用Cglib代理.

5.Cglib的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类.

Cglib动态代理注意事项:

1.需要引入Cglib的jar包

asm.jar  

asm-commons.jar

asm-tree.jar

cglib-2.2.jar

       <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>

2.在内存中动态构建子类,注意代理的类不能被final修饰,否则报错java.lang.ILLegalArgumentException错误.

3.目标对象的方法如果为final/static,那么久不回拦截,即不回执行目标对象的额外业务方法.

cglib代理uml类图:

代码:

package com.hjm.shd.shardingspheredemo.designPatterns.design11.cglibProxy;

public class TeacherDao {

	public String teach() {
		System.out.println(" 老师授课中  , 我是cglib代理,不需要实现接口 ");
		return "hello";
	}
}
package com.hjm.shd.shardingspheredemo.designPatterns.design11.cglibProxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class ProxyFactory implements MethodInterceptor {

	//维护一个目标对象
	private Object target;
	
	//构造器,传入一个被代理的对象
	public ProxyFactory(Object target) {
		this.target = target;
	}

	//返回一个代理对象:  是 target 对象的代理对象
	public Object getProxyInstance() {
		//1. 创建一个工具类
		Enhancer enhancer = new Enhancer();
		//2. 设置父类
		enhancer.setSuperclass(target.getClass());
		//3. 设置回调函数
		enhancer.setCallback(this);
		//4. 创建子类对象,即代理对象
		return enhancer.create();
		
	}
	

	//重写  intercept 方法,会调用目标对象的方法
	@Override
	public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {

		System.out.println("Cglib代理模式 ~~ 开始");
		Object returnVal = method.invoke(target, args);
		System.out.println("Cglib代理模式 ~~ 提交");
		return returnVal;
	}

}
package com.hjm.shd.shardingspheredemo.designPatterns.design11.cglibProxy;

public class Client {

	public static void main(String[] args) {
		//创建目标对象
		TeacherDao target = new TeacherDao();
		//获取到代理对象,并且将目标对象传递给代理对象
		TeacherDao proxyInstance = (TeacherDao)new ProxyFactory(target).getProxyInstance();

		//执行代理对象的方法,触发intecept 方法,从而实现 对目标对象的调用
		String res = proxyInstance.teach();
		System.out.println("res=" + res);
	}

}

代理模式的变体:

1.防火墙代理

内网通过代理穿透防火墙,实现对公网的访问.

2.缓存代理

当请求图片文件等资源时,先到缓存代理取,如果取到资源则ok,否则通过公网或者数据库取,然后缓存

3.远程代理

远程对象的本地代表,通过它可以把远程对象当本地对象来调用.远程代理通过网络和真正的远程对象沟通信息.

4.同步代理

主要通过在多线程编程中,完成多线程间的同步工作.

小结:

以上7种结构型模型到此整理完毕,在此之前已经整理好了5种创建型模型,后续会整理23种设计模式之行为型模型(11种)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值