设计模式之(三) 构造模式

版权声明:欢迎转载,转载请说明出处. 大数据Github项目地址https://github.com/SeanYanxml/bigdata。 https://blog.csdn.net/u010416101/article/details/89761538

前言

离之前的设计模式之(二) 创建者模式的编写有一段时间了. 这部分的内容其实早已经写好. 最近整理出来.

另: 1. 虽都尽量详尽. 但是, 近来回看, 发现书中并不是都对. 也许是个人的理解不同.
2. 这部分的内容, 后续会分开详述.


前言

23种设计模式中7种结构类型模式。其分别是适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式和享元模式。


模式关系

在这里插入图片描述
从宏观来看,适配器模式主要有3种类型,类适配器/对象适配器/接口适配器。其中,类适配器最贴合适配本意。同时,对象适配器引申出了装饰器模式/代理模式/外观模式、桥接模式。(这五种书中称为包管理设计模式)

但是从一定程度上来说,对象适配器,组合模式和享元模式并无直接的联系。(这边感觉原作者画错了。)


适配器模式(Adapter Pattern)

适配器模式(Adapter Pattern)的定义如下:

Convert the interface of a class into another interface clients expert. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces. (将一个类的接口变成客户端所期待的另一种接口,从而使原本因为接口不匹配而无法工作的两个类能够在一起工作。)

  • 适配器模式-通用类图
    adapter-common
// interface - target 目标角色
public interface Target{
	// 目标角色自己的方法
	public void request();
}

// ConcreteTarget - class - 目标角色实现类
public class ConcreteTarget implements Target{
	public void request(){
		System.out.println("Concrete Target Class.");
	}
}

// Adaptee - class - 原角色
public class Adaptee{
	// 原有的业务逻辑
	public void doSomething(){
		// 原有的业务结构
	}
}

// Adapter - class - 适配器角色
public class extend Adaptee implements Target {
	public void request(){
		// 原业务逻辑结构
		doSomething();
	}
}

// Client - class - 使用场景
public class Client{
	public static void main(String []args){
		// 原有业务逻辑
		Target targetConcrete = new ConcreteTarget();
		targetConcrete.request();
		
		// 更改后的业务逻辑
		Target targetAdapter = new Adapter();
		targetAdapter.request();
	}
}
  • Case1 人员系统外接

adapter-case1

// IUserInfo - interface - 内部员工接口
interface IUserInfo{
 public String getUserName();
 public String getHomeAddress();
 public String getMobileNumber();
 public String getOfficeTelNumber();
 public String getJobPosition();
 public String getHomeTelNumber();
}

// UserInfo - class - 内部员工实现
public class UserInfo{
 public String getUserName(){}
 public String getHomeAddress(){}
 public String getMobileNumber(){}
 public String getOfficeTelNumber(){}
 public String getJobPosition(){}
 public String getHomeTelNumber(){}
}

// IOuterUserInfo - interface - 外部员工接口
public interface IOuterUserInfo{
 public Map getUserBaseInfo();
 public Map getUserOfficeInfo();
 public Map getUserHomeInfo();                               
}

// OuterUserInfo - class - 外部员工实现
public class OuterUserInfo implements IOuterUserInfo{
	public Map getUserBaseInfo(){
		Map map = new HashMap();
		map.put("username","username");
		map.put("mobilePoneNumber","1234567890");
		return map;
	}
	
   public Map getUserOfficeInfo(){
   		Map map = new HashMap();
   		map.put("homeTelNumber","1234567890");
   		map.put("homeAddress","ShangHai");
   		return map;
   }
   
   public Map getUserHomeInfo(){
   		Map map = new HashMap();
   		map.put("jobPosition","ShangHai");
   		map.put("officeTelNumber","1234567890");
   		return map;
   }    
}

// 适配器类(适配器模式)
public class OuterUserInfoAdapter extends OuterUserInfo implements IUserInfo	{
	public String getUserName(){return (String) getUserOfficeInfo().get("userName");}
	public String getHomeAddress(){return (String)etUserBaseInfo().get("homeAddress");}
 	public String getMobileNumber(){...}
 	public String getOfficeTelNumber(){...}
 	public String getJobPosition(){...}
 	public String getHomeTelNumber(){...}
}

// 使用场景类
public class Client{
	public static void main(String []args){
		// inner - user
		IUserInfo innerUserInfo = new UserInfo();
		innerUserInfo.getHomeNumber();
		
		// outer - user
		IUserInfo outerUserInfo =  new OuterUserInfoAdapter();
		outerUserInfo.getHomeNumber();
	}
}
// 实际场景中通过 Convert 或者JSON格式进行传输和转换;并且外接方需要提供第三方的工具包(IOuterUserInfo.class OuterUserInfo.class)

适配器模式-优点-缺点-使用场景

优点: 扩展性高 / 适配性强(无须更改可以链接两个不同的类) / 代码复用性高

使用场景: 你有动机修改一个已经投产中的接口时,适配器模式可能是最适合你的模式。

注意事项: 开发阶段基本不需要使用该模式。在维护和重构阶段,经常使用该模式。另开发需要特别注意依赖倒置原则和里氏替换原则。

个人理解
个人理解: 适配器模式是对于项目的一种补充。常常项目投产无法改变时,又需要更改原项目的结构,适配器模式会是一种非常好的方案。有时,我们使用别人提供的类时,我们也经常进行封装,有时也用的是适配器模式。

参考内容:
[1] 设计模式之禅 [2] 23种设计模式全解析

  • 扩展1 - 对象适配器 & 类适配器(Case 1) & 接口适配器

adapter-case2

// 适配器类(适配器模式-对象适配器)
public class OuterUserInfoAdapter implements IUserInfo{
	public IOuterBaseInfo baseInfo = null;
	public IOuterHomeInfo homeInfo = null;

	public OuterUserInfoAdapter(IOuterBaseInfo baseInfo, IOuterHomeInfo homeInfo){
	  this.baseInfo = baseInfo;
	  this.homeInfo = homeInfo;
	}
	
	public String getUserName(){return (String) baseInfo. getUserOfficeInfo().get("userName");}
	public String getHomeAddress(){return (String) homeInfo.getUserBaseInfo().get("homeAddress");}
 	public String getMobileNumber(){...}
 	public String getOfficeTelNumber(){...}
 	public String getJobPosition(){...}
 	public String getHomeTelNumber(){...}
}
  • 扩展2 - 接口适配器

使用抽象类来解决接口中需要实现的方法过多的问题。

interface IUserInfo{
	public String method1();
	public String method2();	
}

public abstract class AbstactUserInfo implements IUserInfo{
	public String method1(){}
	public String method2(){}
}

public class UserInfo1 extends AbstactUserInfo{
	public String method1(){
		// do something method1
	}
}

public class UserInfo2 extends AbstactUserInfo{
	public String method2(){
		// do something method2
	}
}

publc class Client {
	public static void main(String []args){
		IUserInfo userInfo1 = new UserInfo1();
		userInfo1.method1();
		userInfo1.method2();

		IUserInfo userInfo2 = new UserInfo2();
		userInfo2.method1();
		userInfo2.method2();	
	}
}

装饰器模式(Decorator Pattern)

装饰模式(Decorator Pattern)是一种比较常见的模式,其定义如下:

Attach additional responsibilities to an object dynamically keeping the same interface. Decorators provide a flexible alternative to subclassing for extending functionally.(事业相同的接口动态的给一个对象增加一些额外的功能,就增加功能来说,装饰模式相比生成子类更为灵活。)

  • 装饰器模式 - 通用类图

decorator-common

// Component - abstact class - 抽象构件类
public abstract class Component{
	// 抽象的方法
	public abstract void operate();
}

// ConcreteComponent - class - 实体构件类
public class ConcreteComponent extends Component{
	public abstract void operate(){
		// do Something()
	}
}

public abstract class Decorator extends Component{
	private Component component = null;
	public Decorator(Component component){
		this.component = component;
	}
	
	public void operate(){
		this.component.operate():
	}
}

// 装饰类1
public class ConcreteDecorator extends Decorator{
	public void decoratorMethod(){}
	public void decoratorMethod2(){}
	public void operate(){
		// 方法前装饰
		decoratorMethod();
		this.component.operate():、
		// 方法后装饰
		decoratorMethod2();
	}
}

// 装饰类2
public class ConcreteDecorator2 extends Decorator{
	public void decoratorMethod(){}
	public void decoratorMethod2(){}
	public void operate(){
		// 方法前装饰
		decoratorMethod();
		this.component.operate():、
		// 方法后装饰
		decoratorMethod2();
	}
}

public class Client{
	public static void main(String []args){
		Component concreteComponent = new ConcreteComponent();
		
		Component decoratorComponent1 = new ConcreteDecorator(concreteComponent);
		decoratorComponent1.operate();
		
		Component decoratorComponent2 = new ConcreteDecorator2(concreteComponent);
		decoratorComponent2.operate();
	}
}
  • Case1 - 汇报成绩

优点 & 缺点 & 使用场景 & 注意事项

优点:1. 装饰类和被装饰类隔离 2. 继承的另一种实现方式 3. 动态扩展一个类的执行

缺点:1. 继承不能太多,否则难以维护 (多层的装饰是比较复杂的)

使用场景: 1. 动态的添加功能和删除功能 2. Spring AOP的切点前和后(个人感觉就是使用了装饰器模式) 3. 软件插件(例如 Mybatis的分页插件)

个人理解

装饰器模式特别像是对象适配器扩展而来。在开发过程中,需要在方法执行前和后做一些触发和更新,就是使用了装饰器模式的思想。

此外,在开发过程中,经常会遇到增加功能的情况,这时使用装饰器模式就较为合适。(当然,通过继承,复写也可以实现。但是继承的可维护性和扩展性,在某些特定的场景下并不如装饰器模式。)


代理模式(Proxy Pattern)

代理模式(Proxy Pattern)是一个使用率非常高的模式,其定义如下:

Provides a surrogate(代理) or placeholder(占位符) for another object to control access to it.(为其它对象提供一种代理以控制这个对象的访问)

  • 代理模式-通用类图

proxy-common

# Subject - interface - 抽象主题类
interface Subject{
	// 定义一个方法
	public void request();
}

# RealSubject - class - 具体主题类
class RealSubject implements Subject{
	// 实现接口方法
	public void request(){}
}

# Proxy - class - 代理类
public class Proxy implements Subject{
	private Subject subject = null;
	// 默认代理
	public Proxy(){this.subject = new RealSubject;}
	// 定义代理者
	public Proxy(Subject subject){this.subject = subject;}
	
	// before
	public void uniqueMethod(){}
	
	// 接口方法
	public void request(){
		uniqueMethod();
		subject. request();
	}
}
  • 动态代理模式-通用类图

proxy-dynamic-common

# interface - Subject - 抽象主题
interface Subject{
	// 业务操作
	public void doSomething();
}

# RealSubject - class - 真实主题
class RealSubject implements Subject{
	// 业务操作
	public void doSomething(){}
}

# MyInvocationHandler - class - 动态代理Handler类
class MyInvocationHandler implements InvocationHandler {
	// 被代理的对象
	private Object target = null;
	// 通过构造函数给予一个对象
	public MyInvocationHandler(Obj _obj){
		this.obj = _obj;
	}
	
	// 代理方法
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
		// 执行被代理的方法
		return method.invoke(this.target, args);
	}
}

# DynamicProxy - class - 动态代理类
public class DynamicProxy<T> {
	public static <T> T newProxyInstance(ClassLoader loader, Class<?> [] interfaces, InvocationHandler h){
		// 寻找 JoinPoint链接点 AOP框架使用元数据定义
		if(true){
			// 执行一个前置通知
			(new BeforeAdvice()).exec();
		}
		return (T)Proxy.newProxyInstance(loader,interfaces,h);	
	}	
}

// IAdvice - 接口通知和实现代码
interface IAdvice{
	// 通知方法
	public void exec();
}

public class BeforeAdvice implements IAdvice{
	public void exec(){}
}

// Client - 动态代理场景类
class Client{
	public static void main(String []args){
		// 定义一个主题
		Subject subject = new RealSubject();
		// 定义一个Handler
		InvocationHandler handler = new MyInvocationHandler(subject);
		
		// 定义主题的代理
		Subject proxy = DynamicProxy.newProxyInstance(subject.getClass().getClassLoader(),subject.getClass.getInstance(),Handler);
		
		// 代理的行为
		proxy.doSomething("Finish");
	}
}


# 进一步封装
public class SubjectDynmaicProxy extends DynamicProxy{
	public static <T> T newProxyInstance(Subject subject){
	// 参数不同的 重载函数
	//(ClassLoader loader, Class<?> [] interfaces, InvocationHandler h)
		ClassLoader loader =  subject.getClass().getClassLoader();
		Class<?>[] classes = subject.getClass().getInteraces();
		// 获取Hander
		InvocationHandler handler = new MyInvocationHandler(subject);
		return (T)Proxy.newProxyInstance(loader,interfaces,handler);	
	}
}
  • Case1 游戏角色 - 代练

普通玩家玩游戏

proxy-case1

interface IGamePlayer{
	public login(String user, String password);
	public void killBoss();
	public void upgrade();
}

class GamePlayer implements IGamePlayer{
	public login(String user, String password){}
	public void killBoss(){}
	public void upgrade(){}
}

public class Client{
	static void main(String []args){
		IGamePlayer gamePlayer = new GamePlayer();
		gamePlayer.login("hello","123");
		gamePlayer.killBoss();
		gamePlayer.upgrade();
	}
}

代练代理游戏

proxy-case1-1

# GamePlayerProxy - class
public class GamePlayerProxy implements IGamePlayer{
	private IGamePlayer gamePlayer = null;
	public GamePlayerProxy(IGamePlayer gamePlayer){
		this.gamePlayer = gamePlayer;
	}
	public login(String user, String password){
		gamePlayer.login(user, password);
	}
	public void killBoss(){
		gamePlayer.killBoss();
	}
	public void upgrade(){
		gamePlayer.upgrade();
	}
}

public class Client{
	static void main(String []args){
		IGamePlayer gamePlayer = new GamePlayer();
		IGamePlayer gamePlayerProxy = new GamePlayerProxy(gamePlayer);
		// 代练
		gamePlayerProxy.login("hello","123");
		gamePlayerProxy.killBoss();
		gamePlayerProxy.upgrade();
	}
}

  • Case2 游戏角色 - 普通代理

直接代理 和 对象代理 (代理优化 不直接接触原始对象)

# GamePlayerProxy - class
public class GamePlayerProxy implements IGamePlayer{
	private IGamePlayer gamePlayer = null;
	public GamePlayerProxy(String name){
		this.gamePlayer = new GamePlayer(name);
	}
	public GamePlayerProxy(IGamePlayer gamePlayer){
		this.gamePlayer = gamePlayer;
	}
	public login(String user, String password){
		gamePlayer.login(user, password);
	}
	public void killBoss(){
		gamePlayer.killBoss();
	}
	public void upgrade(){
		gamePlayer.upgrade();
	}
}

public class Client{
	static void main(String []args){
		// 构建方式1
		IGamePlayer gamePlayer = new GamePlayer();
		IGamePlayer gamePlayerProxy = new GamePlayerProxy(gamePlayer);
		
		// 构建方式2 - 不直接接触对象
		IGamePlayer gamePlayerProxy = new GamePlayerProxy(“ZhangShan”);
		
		// 代练
		gamePlayerProxy.login("hello","123");
		gamePlayerProxy.killBoss();
		gamePlayerProxy.upgrade();
	}
}
  • Case3 代理特性

代理可以拥有自己的特性

# ProxyFunction
interface ProxyInterface{
	public void proxyFunction();
}

# GamePlayerProxy - class
public class GamePlayerProxy implements IGamePlayer,ProxyInterface{
	private IGamePlayer gamePlayer = null;
	public GamePlayerProxy(IGamePlayer gamePlayer){
		this.gamePlayer = gamePlayer;
	}
	public login(String user, String password){
		gamePlayer.login(user, password);
	}
	public void killBoss(){
		gamePlayer.killBoss();
	}
	public void upgrade(){
		gamePlayer.upgrade();
	}
		
	public void proxyFunction(){}
}

public class Client{
	static void main(String []args){
		IGamePlayer gamePlayer = new GamePlayer();
		IGamePlayer gamePlayerProxy = new GamePlayerProxy(gamePlayer);
		// 代练
		gamePlayerProxy.login("hello","123");
		gamePlayerProxy.killBoss();
		gamePlayerProxy.upgrade();
	}
}
  • Case4 - 动态代理

动态代理游戏

dynamic-case4

# GamePlayerIH
public class GamePlayerIH implements InvocationHandler{
	// 被代理者
	Class class = null;
	// 被代理者实例
	Object obj = null;
	
	public  GamePlayerIH(Object _obj){
		obj = _obj;
	}
	
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
		Object result = method.invoke(this.obj, args);
		return result;
	}
}

// Client - 动态代理场景类
class Client{
	public static void main(String []args){
		// 定义一个主题
		IGamePlayer subject = new GamePlayer();
		// 定义一个Handler
		InvocationHandler handler = new GamePlayerIH(subject);
		
		// 定义主题的代理
		IGamePlayer proxyGamePlayer = DynamicProxy.newProxyInstance(subject.getClass().getClassLoader(),subject.getClass.getInstance(),Handler);
		
		// 代理的行为
		proxyGamePlayer.login("ZhangShan","123");
		proxyGamePlayer.upgrade():
	}
}


优点 & 缺点 & 注意点 & 使用场景

优点: 职责清晰/高扩展性/(配置-映射-自动装载-AOP)

使用场景: 静态代理/动态代理

个人理解:

代理模式本意是委托其他对象进行操作,种类较多,包括:普通代理、强制代理和动态代理。在框架的构建角度,动态代理和普通代理都是使用较多的。其次,代理仅仅是代理。我们有时也会在代理前后进行扩展,这也就是装饰器模式。实践中通常和装饰器模式混用。最后,我们在使用类似Spring AOP的模式的时候,其实已经用到了代理模式。

代理模式

Questions

Q1: 代理模式和装饰器模式的区别?
详见 对象适配器5种类型比较。

Q2: 动态代理中InvocationHandler是什么? 经常被怎么样的进行使用?

Q3: 相关技术Spring AOPAspectJCGLIB等了解。


外观模式(Facade Pattern)

门面模式(Facade Pattern)也叫做外观模式,是一种比较常用的封装模式。其定义如下:

Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.(要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。)

  • 外观模式 - 通用类图

facade-common

#所有的外部系统统一称为外部系统(SubSystemClasses)

# ClassA - Class - SubSystemClass
class ClassA{
	public void doSomethingA(){}
}

# ClassB - Class - SubSystemClass
class ClassB{
	public void doSomethingB(){}
} 

# ClassC - Class - SubSystemClass
class ClassC{
	public void doSomethingC(){}
}

# Facade - class
class Facade{
	// 被委托的对象
	private ClassA classA = new ClassA();
	private ClassB classB = new ClassB();
	private ClassC classC = new ClassC();
  	
	public void doSomethingA(){
		classA.doSomethingA();
	}
	
	public void doSomethingB(){
		classB.doSomethingB();
	}
	
	public void doSomethingC(){
		classC.doSomethingC();
	}
	
	public void doSomething(){
		classA.doSomethingA();
		classB.doSomethingB();
		classC.doSomethingC();
	}
}

动态委托-代理

  • Case1 - 原始发送邮件

facade-case1

# ILetterProcess - interface
interface ILetterProcess{
	public void writeContext(String context);
	public void fillEnvelope(String address);
	public void letterInotoEnvelope();
	public void sendLetter();
}

# LetterPorcessImpl - class
class LetterProcessImpl{
	public void writeContext(String context){}
	public void fillEnvelope(String address){}
	public void letterInotoEnvelope(){}
	public void sendLetter(){}
}

# Client - class
class Client{
	public static void main(String []args){
		ILetterProcess letterProcess = new LetterProcessImpl();
		// 执行写信过程
		// 写信内容
		letterProcess.writeContext("123");
		// 写信封
		letterProcess.fillEnvelope("ShangHai");
		// 装信 
		letterProcess.letterInotoEnvelope();
		// 发送信件
		letterProcess.sendLetter();
	}
}
  • Case2 - 邮局发送邮件

上述的过程过于麻烦,邮局推出邮件代写。只要提供邮件内容和地址即可。邮局即为外观模式中的门面(也叫外观)。

facade-case2

# PostOffice - class - 门面类
class PostOffice{
	private ILetterProcess letterProcess = new LetterProcessImpl();
	// 门面方法
	public void sendLetter(String context,String address){
		letterProcess.writeContext(context);
		letterProcess.fillEnvelope(address);
		letterProcess.letterInotoEnvelope();
		letterProcess.sendLetter();
	}
}

class Client{
	public static void main(String []args){
		PostOffice postOffice = new PostOffice();
		// 门面发送邮件
		postOffice.sendletter("你好啊xxx","ShangHai");
	}
}
  • Case3 - 邮局发送邮件(业务更新)

业务更新,先装信再写信封。(只需要更改门面内封装的方法顺序即可。)

# PostOffice - class - 门面类
class PostOffice{
	private ILetterProcess letterProcess = new LetterProcessImpl();
	// 门面方法
	public void sendLetter(String context,String address){
		letterProcess.writeContext(context);
		// 业务更新
		letterProcess.letterInotoEnvelope();
		letterProcess.fillEnvelope(address);
		letterProcess.sendLetter();
	}
}

class Client{
	public static void main(String []args){
		PostOffice postOffice = new PostOffice();
		// 门面发送邮件
		postOffice.sendletter("你好啊xxx","ShangHai");
	}
}
  • 优点 & 缺点 & 使用场景 & 注意事项

优点: 提高安全性(门面类统一控制访问);减少耦合,由门面统一管理;方便单元测试;

缺点: 不符合开闭原则(难以继承和复写)

使用场景:黑盒/单一接口提供外界访问/子系统相对独立/项目的保密性。

  • 扩展- 多门面系统

根据项目的规模和需求,有时需要扩充为多个门面的存在。

  • 个人理解

根据个人理解,门面模式通常用于向第三方提供服务时,对于本系统提供单一独立的接口时使用。门面模式的关键点在于隔离、封装和方便管理。缺点也非常明显,那就是难以继承和维护。(另:个人感觉门面模式和代理模式不是特别容易区别。)


桥接模式(Bridge Pattern)

桥梁模式(Bridge Pattern)也叫做桥接模式。是一种比较简单的模式,其定义如下:

Decouple an abstraction from its implementation so that two can vary independently.(将抽象和实现解耦,使得两者可以独立变化。)

  • 桥接模式 - 通用类图

bridge-comon

Abstraction: 抽象化角色

RefinedAbstraction: 具体抽象化角色

Implementor: 实现化角色

ConcreteImplementor: 具体实现化角色

# Implementor -interface - 实现化角色
public interface Implementor{
	public void doSomething();
	public void doAnything();	
}

# ConcreteImplementor - class - 具体实现化角色
public class ConcreteImplementor1 implements Implementor{
	public void doSomething(){}
	public void doAnything(){}
}

# Abstraction - abstract class - 抽象化角色
public abstract class Abstraction{
	private Implementor implementor;
	public Abstraction(Implementor implementor){
		this.implementor = implementor;
	}
	
	public void request(){
		implementor.doSomething();
	}
	
	public Implementor getImp(){
		return Implementor;
	}
}

# RefinedAbstaction
class RefinedAbstaction extends Abstraction{
		private Implementor implementor;
	public Abstraction(Implementor implementor){
		super(implementor);
	}
	
	public void request(){
		getImp().doSomething();
	}
	
	public Implementor getImp(){
		return getImp();
	}
}

# Client - class

public class Client{
	public static void main(String []args){
		// 定义一个实现化角色
		Implementor imp = new ConcreteImplementor();
		// 定义一个抽象化觉色
		Abstraction abstract = new RefinedAbstaction(imp);
		
		// 执行行为
		abstract.request();
		
	}
}  
  • Case1 - 工厂生产

bridge-case1

# 抽象工厂
public abstract class Corp{
	public abstract void makeMoney();
	public abstract void produce();
	public abstract void sell();
}

# 实际工厂1-服装厂
public ClothesCorp extends Corp{
	public void makeMoney(){}
	public void produce(){}
	public void sell(){}
}

# 实际工厂2-地产
public HouseCorp extends Corp{
	public void makeMoney(){}
	public void produce(){}
	public void sell(){}
}

# 需求变化 重新定义一个手机厂
public MobilePhoneCorp extends Corp{
	public void makeMoney(){}
	public void produce(){}
	public void sell(){}
}

注: 当厂型转换时,会遗留一系列的对象。比如HouseClothesMobilePhone这写类有时不便于管理。于是,可以将厂--产品的模式抽离出来,也就是桥接模式。两端可以自由变化,甚至可以手机厂生产衣服!

  • Case2 - 工厂生产(桥接变化)

bridge-case2

# Product
public abstract class Product{
	public abstract void beProducted();
	public abstract void beSelled();
}

# Factory
public abstract class Corp{
	private Product product;
	public Corp(Product product){
		this.product = product;
	}
	public abstract void makeMoney();
}

# 产品变化
public class Clothes extends Product{
	public void beProducted(){}
	public void beSelled(){}
}
public class House extends Product{
	public void beProducted(){}
	public void beSelled(){}
}

# 工厂变化
public ClothesCorp extends Corp{
	public ClothesCorp(House house){
		super(house);
	}
	public void makeMoney(){
	}
}

public ShanZhai extends Corp{
	public ShanZhai(Product product){
		super(product);
	}
	public void makeMoney(){
	}
}

# Client
public class Client{
	public static void main(String []args){
		Product house = new House();
		ShanZhai shanzhai = new ShanZhai(house);
		shanzhai.makeMoney();
	}
}

[java] view plaincopy
public interface Sourceable {  
    public void method();  
}  
分别定义两个实现类:

[java] view plaincopy
public class SourceSub1 implements Sourceable {  
  
    @Override  
    public void method() {  
        System.out.println("this is the first sub!");  
    }  
}  
[java] view plaincopy
public class SourceSub2 implements Sourceable {  
  
    @Override  
    public void method() {  
        System.out.println("this is the second sub!");  
    }  
}  
定义一个桥,持有Sourceable的一个实例:

[java] view plaincopy
public abstract class Bridge {  
    private Sourceable source;  
  
    public void method(){  
        source.method();  
    }  
      
    public Sourceable getSource() {  
        return source;  
    }  
  
    public void setSource(Sourceable source) {  
        this.source = source;  
    }  
}  
[java] view plaincopy
public class MyBridge extends Bridge {  
    public void method(){  
        getSource().method();  
    }  
}  
测试类:

[java] view plaincopy
public class BridgeTest {  
      
    public static void main(String[] args) {  
          
        Bridge bridge = new MyBridge();  
          
        /*调用第一个对象*/  
        Sourceable source1 = new SourceSub1();  
        bridge.setSource(source1);  
        bridge.method();  
          
        /*调用第二个对象*/  
        Sourceable source2 = new SourceSub2();  
        bridge.setSource(source2);  
        bridge.method();  
    }  
}  
output:

this is the first sub!
this is the second sub!

# Question: Bridge会进行如何变化?

优点 & 缺点 & 使用场景

优点: 抽象和实现分离;优秀的扩充能力;实现细节对用户透明。

使用场景:

  1. 不希望或不适用使用继承的场景;
  2. 接口或抽象类不稳定的场景;
  3. 重用性粒度较高的场景。

注意事项:多个族之间的关系时(多个维度变化);N层继承时(N层继承也不一定需要用桥接)。

个人理解:

桥接模式的核心在于解耦,抽象和抽象依赖。这样接口&抽象都可以自己的独立变化。适合两种主要类型X * X种变化的体现。在使用过程中,可以做到多次桥接(X * X * X * X)。

比如本文的工厂与产品模式中,工厂和产品都需要进行变化并进行交互时,将原继承模式下的服装公司/房子公司中的房子/服装抽离出来。

另:此处说的多层继承时指房子-花房子-白色花房子可以将其抽象成三种特性,房子-花-颜色的抽象,这样可以进行各种组合,比如房子-树-绿房子-水-蓝这样的组合,而不用过度的进行继承来实现这样的桥接变化。

Q&A

Q1: JDBC的桥接用过么,说下其中关于桥接的具体实现?

参考文献
[1].桥梁模式应用场景
[2]. 设计模式-桥梁模式


组合模式(Composite Pattern)

组合模式(Composite Pattern)也叫合成模式,有时又叫做部分-整体模式(Part-Whole),主要用来描述部分和整体之间的关系,其定义如下:

Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.(将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一支性。)

  • 组合模式 - 通用类图

composite-common

Component 抽象构建角色:含有组合对象的公共方法和属性。

Leaf: 叶子构件。最小的构建,没有其他的分支。

Composite: 树枝构件。组合构件树枝和叶子结点的合集。

# Component - abstract class
public abstract class Component{
	// 公共属性和方法
	public void doSomething(){}
}

# Leaf - class
public class Leaf extends Component{
	// 覆写方法
	public void doSomething(){}
}

# Composite - class
public Composite extends Component{
	private ArrayList<Component> arrylist = new ArrayList<>();
	public void Add(Component component){
		arrylist.add(component);
	}
	public void Remove(Component component){
		arrylist.remove(component);
	}
	public Component GetChild(int i){
		return arrylist.get(i);
	}
	
	// 覆写方法
	public void doSomething(){}
}

# Client - class
public class Client{
	public static void main(String []args){
		Component root = new Composite();
		Component branch = new Composite();
		Component leaf = new Leaf();

		root.add(branch);
		branch.add(leaf);
	}
}

  • Case 1-3

原始的例子是将 各个类都独立的分成一个类。但是Root/Leaf/Branch等都有公共的抽象,所以可以把这部分的内容抽离出来。

composite-case1-3

# Corp
public abstract class Corp{
	private String name = "";
	private String position = "";
	private int salary = 0;
	public corp(String _name, String _position, int _salary){
		name = _name;
		position = _position;
		salary = _salary;  
	}
	public String getInfo(){
		return name+"#"+position+"#"+salary;
	}
}

# Leaf
public class Leaf extends Corp{
	public Leaf(String _name, String _position, int _salary){
		super(_name,_position,_salary);
	}
}

# Branch
public class Branch extends Corp{
	ArrayList<corp> subordinateList = new ArrayList<Corp>();
	public Branch(String _name, String _position, int _salary){
		super(_name,_position,_salary);
	}
	
	public void addSubordinate(Corp corp){
		subordinateList.add(corp);
	}
	
	public ArrayList<Corp> getSubordinate(){
		return this.subordinateList;
	}
}

public class Client{
	public static void main(String []args){
		// 构建一棵树
	}
	
	//遍历一棵树
	public static Strig getTreeInfo(Branch root){
		ArrayList<corp> subordinateList = root.getSubordinate();
		String info = "";
		for(Corp s : subordinateList){
			if(s instanceof Leaf){
				info = info + s.getInfo + "\n ";
			}else{
				info = info + s.getInfo + "\n" + getTreeInfo((Branch)s);
			}
		}
		return info;
	}
}
  • 优点 & 缺点 & 使用场景 & 注意事项

优点: 高层模块调用简单。树枝和树叶使用同一个抽象。(可以认为,树枝既是树枝,也是树叶。) / 节点自由增加。(这个其实是对象内数组决定)

缺点: 组合模式是对依赖倒转的破坏。

使用场景: 导航栏 / 树形菜单 / 树形文件夹

注意事项: 主要是树形结构,就要考虑组合模式。只要体现局部和整体的关系的时候,而且这种关系还可能比较深,那么需要考虑使用组合模式。

  • Q & A

Q1: (数据库)数据库内的树形结构应该如何存储,如何遍历?

Q2: 子类可以有多个父类么,复杂度如何?

Q3: (算法题)查询某个子类的父节点应该如何处理?

Q4: 组合的安全模式和透明模式分别指什么,能详细说明下么?

Q5: 树形结构的遍历了解么?
A: 前序遍历,后续遍历,中序遍历(详细了解 看树的数据结构)

Q6: JDOM 与 DOM4J中组合模式的使用?(是否有使用过类似的内容)


享元模式(Flyweight Pattern)

享元模式(Flyweight Pattern)是池技术的重要实现方式,其定义如下:

Use sharing to support large numbers of fine-grained objects efficiently. (使用共享对象可有效地支持大量的细粒度的对象。)

  • 享元模式 - 通用类图

flyweight-common

#Flyweight - 抽象享元角色
public abstract class Flyweight{
	//内部状态
	private String instrinsic;
	//外部状态
	protected final String extrinsic;
	
	public Flyweight(String _extrinsic){
		this.extrinsic = _extrinsic;
	}
	
	// 抽象业务操作
	public abstract void operate();
	 
	// 内部状态GET/SET
	public String getInstrinsic(){return instrinsic;}
	public String setInstrinsic(String instrinsic){
		this.instrinsic = instrinsic;
	}
}

// 具体享元角色1
public class ConcreteFlyweight1 extends Flyweight{
	public ConcreteFlyweight1(String _extrinsic){
		super(_extrinsic);
	}
	public abstract void operate(){}
}

// 具体享元角色2
public class ConcreteFlyweight2 extends Flyweight{
	public ConcreteFlyweight1(String _extrinsic){
		super(_extrinsic);
	}
	public abstract void operate(){}
}

// 享元工厂
public class FlyweightFactory{
	// 定义一个池容器
	private static HashMap<String,Flyweight> pool = new HashMap<>();
	
	// 享元工厂
	public static Flyweight getFlyweight(String _extrinsic){
		Flyweight flyweight = null;
		// 池中是否已有该对象
		if(pool.containsKey(_extrinsic)){
			// 池中有该对象
			flyweight = pool.get(_extrinsic);
		}else{
			// 池中无该对象
			flyweight = new ConcreteFlyweight1(_extrinsic);
			pool.put(_extrinsic, flyweight);
		}
		return flyweight;
	}
}
  • Case1 - 报考信息系统

flyweight-case1

# SignInfo
public class SignInfo{
	private String id;
	private String location;
	private String subject;
	private String postAddress;
	// Getter() &  Setter();
}

# SigninfoFactory
public class SignInfoFactory{
	public static SignInfo getSignInfo(){
		return new SignInfo();
	}
}

# Client
public class Client{
	public static void main(String []args){
		SignInfo signinfo =  SignInfoFactory.getSignInfo(); 
	}
}
  • Case2 - 报考信息系统 - 享元

flyweight-case1

# SignInfoKey
class SignInfoKey extends SignInfo{
  prvate String key;
  public SignInfoKey(String key){
 		this.key = key;
  }
  // Getter - Setter
}

#SignInfoFactory
public class SignInfoFactory{
	private static HashMap<String, SignInfo> pool = new HashMap<>();
	public static SignInfo getSignInfo(String key){
		SignInfo result = null;
		if(!pool.containsKey(key)){	
			result = new SignInfoKey(key);
		}else{
			result = pool.get(key);
		}
		return result;
	}
}

#Client
public class Client{
	public static void main(String []args){
		// 可以使用随机使用Key
		String Key = 1 ;
		// 线程池获取享元对象
		SignInfo signInfo = SignInfo.getSignInfo(key);
	}
}
  • 优点 & 缺点 & 使用场景

优点: 减少相似对象的创建,提高程序的性能。

缺点: 将对象分离成内部对象,外部对象。(内部是对象的内部属性,外部对象是指Map的Key值,即用于标示对象唯一性的键)

使用场景: 系统内存在大量类似对象/需要缓冲池的场景/内部对象当作外部key值使用时。

Q&A

Q1: 熟悉常用的池类型么,了解Cmmon-pool框架么?
了解下ThreadPoolExecutor即可.

Q2: 享元模式在多线程模式下,应该如何保持线程安全?
使用线程安全类ConcurrentHashMap. 使用锁机制.使用状态管理.

展开阅读全文

没有更多推荐了,返回首页