GOF23种设计模式在Java中的应用(part 2)

本文接续part1:GOF23种设计模式在Java中的应用(part 1)

结构型模式

结构型模式:
– 核心作用:是从程序的结构上实现松耦合,从而可以扩大整体的类结
构,用来解决更大的问题。
– 分类:
• 适配器模式、代理模式、桥接模式、
装饰模式、组合模式、外观模式、享元模式

目录

2.6 适配器模式(Adapter Pattern)

2.7 代理模式(Proxy Pattern):

2.8 桥接模式(Bridge Pattern)

2.9 组合模式(Composite Pattern)

2.10 装饰模式(Decorator Pattern)

2.11 外观模式(Facade Pattern)

2.12 享元模式(Flyweight Pattern)

2.13 结构型模式小结:


2.6 适配器模式(Adapter Pattern)

2.6.1 适配器模式定义:

将一个类的接口转换成客户希望的另外一个接口。adapter模式使得原本由于接口不兼容而不能在一起工作的那些类可以在一起工作。

适配器模式又叫变压器模式,也叫包装模式(wrapper),但是包装模式不止一个,还包括装饰模式。

模式中的角色
– 目标接口(Target):客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。
– 需要适配的类(Adaptee):需要适配的类或适配者类。
– 适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口。 

适配器模式通用类图

 

 Target目标角色

该角色定义把其他类转换为何种接口,也就是我们期望的接口。

Adaptee源角色

你想把谁转换成目标角色,这个“谁”就是源角色,它是已经存在的、运行良好的类或对象,经过适配器角色的包装,它会成为一个崭新的、靓丽的角色。

Adapter适配器角色

适配器模式的核心技术,其他两个角色都是已经存在的角色,而适配器角色是需要新建立的,它的职责非常简单:把源角色转换成目标角色,怎么转换?通过集成或是类关联的方式。

2.6.2 适配器模式的优点:

优点:

适配器模式可以让两个没有关系的类在一起运行,只要适配器这个角色能搞定他们就成。

增加了类的透明性。现象看,我们访问的target目标角色,但是具体的实现都委托给了源角色,而这些对高层次模块是透明的,也是它不需要关心的。

提高了类的复用率。当然了,源角色在原有的系统中还是可以正常使用,而在目标角色中也可以充当新的演员。

灵活性非常好。某一天,突然不想要适配器了,没问题,删掉这个适配器就可以了,其他代码都不用修改,基本上就类似于一个灵活的构件,想用就用,不用卸载。

2.6.3 适配器模式解决方案

下面的场景,如何解决?

适配器模式解决方案:

 

具体实现代码:

类适配器:

class Adapter extends Adaptee implements Target{
public void request() {
super.specificRequest();
}
}

对象适配器:

class Adapter implements Target{
private Adaptee adaptee;
public Adapter (Adaptee adaptee) {
this.adaptee = adaptee;
}
public void request() {
this.adaptee.specificRequest();
}
}

 完整实例演示适配器模式:

1、adapatee类

package cn.xiaomifeng1010.adapter;

/**
 * 被适配的类
 * 
 * @author Administrator
 *
 */
public class Adaptee {
	
	public void request(){
		System.out.println("可以完成客户请求的需要的功能!");
	}
}

2、target类

package cn.xiaomifeng1010.adapter;

public interface Target {
	void handleReq();
}

3.adapter类

package cn.xiaomifeng1010.adapter;

/**
 * 适配器 (类适配器方式)
 * (相当于转接器)
 * @author Administrator
 *
 */
public class Adapter extends Adaptee implements Target {
	
	
	@Override
	public void handleReq() {
		super.request();
	}
	
}
package cn.xiaomifeng1010.adapter;

/**
 * 适配器 (对象适配器方式,使用了组合的方式跟被适配对象整合)
 * (相当于的转接器)
 * @author Administrator
 *
 */
public class Adapter2  implements Target {
	
	private Adaptee adaptee;
	
	@Override
	public void handleReq() {
		adaptee.request();
	}

	public Adapter2(Adaptee adaptee) {
		super();
		this.adaptee = adaptee;
	}
	
	
	
}

4、client端

package cn.xiaomifeng1010.adapter;

/**
 * 客户端类
 * (相当于例子中的笔记本,只有USB接口)
 * @author Administrator
 *
 */
public class Client {
	
	public void test1(Target t){
		t.handleReq();
	}
	
	public static void main(String[] args) {
		Client  c = new Client();
		
		Adaptee a = new Adaptee();
		
//		Target t = new Adapter();

		Target t = new Adapter2(a);
		
		c.test1(t);
		
	}
	
}

2.6.4 适配器使用场景:

工作中的场景
– 经常用来做旧系统改造和升级
– 如果我们的系统开发之后再也不需要维护,那么很多模式都是没必要的,但是不幸的是,事实却是维护一个系统的代价往往是开发一个系统的数倍。
• 我们学习中见过的场景
– java.io.InputStreamReader(InputStream)
– java.io.OutputStreamWriter(OutputStream)

2.7 代理模式(Proxy Pattern):

2.7.1 代理模式定义:

为其他对象提供一种代理以控制这个对象的访问。

通过代理,控制对对象的访问!
可以详细控制访问某个(某类)对象的方法,在调用这个方法前做前置处理,调用这个方法后
做后置处理。(即:AOP的微观实现!)
– AOP(Aspect Oriented Programming面向切面编程)的核心实现机制!

代理模式通用类图

 

Subject抽象主题角色

抽象主题角色可以是抽象类也可以是接口,是一个普通的业务类型定义,无特殊要求。

RealSubject 具体主题角色(真实角色)

也叫被委托角色,被代理角色。它才是冤大头,是业务逻辑的具体执行者。实现抽象角色,定义真实角色索要实现的业务逻辑,供代理角色调用(关注真正的业务逻辑)

proxy代理主题角色

也叫委托类,代理类。它是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作(在真实角色处理完毕前后做预处理和善后处理工作)。

将统一的流程控制放到代理角色中处理。

 2.7.2 代理模式的优点

职责清晰

真实角色就是实现实际的业务逻辑,不用关心其他非本职工作的事物,通过后期的代理完成一件事物,附带的结果就是编程简介清晰。

高拓展性

具体主题角色是随时都会发生变化的,只要它实现了接口,甭管它如何变化,都逃不过如来佛的手掌(接口),那我们的代理类完全可以在不做任何修改的情况下使用。

智能化

这在我们前面的讲解中没有体现出来,Struts2把表单元素映射到对象中。

2.7.3 代理模式应用场景:

应用场景:
– 安全代理:屏蔽对真实角色的直接访问。
– 远程代理:通过代理类处理远程方法调用(RMI)
– 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象。
• 比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时不可能将所有的图片都显示出来,这样就可以
使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。
• 分类:
– 静态代理(静态定义代理类)
– 动态代理(动态生成代理类)
• JDK自带的动态代理
• javaassist字节码操作库实现
• CGLIB
• ASM(底层使用指令,可维护性较差)

2.7.4 静态代理

实例演示:

明星与代理公司(经纪公司)签约例子

1、Star

package cn.xiaomifeng1010.proxy.staticProxy;

public interface Star {
	/**
	 * 面谈
	 */
	void confer();
	/**
	 * 签合同
	 */
	void signContract();
	/**
	 * 订票
	 */
	void bookTicket();
	/**
	 * 唱歌
	 */
	void sing();
	/**
	 * 收钱
	 */
	void collectMoney();
}

 2、RealStar

package cn.xiaomifeng1010.proxy.staticProxy;

public class RealStar implements Star {

	@Override
	public void bookTicket() {
		System.out.println("RealStar.bookTicket()");
	}

	@Override
	public void collectMoney() {
		System.out.println("RealStar.collectMoney()");
	}

	@Override
	public void confer() {
		System.out.println("RealStar.confer()");
	}

	@Override
	public void signContract() {
		System.out.println("RealStar.signContract()");
	}

	@Override
	public void sing() {
		System.out.println("RealStar(周杰伦本人).sing()");
	}
	
	
	
}

3、ProxyStar

package cn.xiaomifeng1010.proxy.staticProxy;

public class ProxyStar implements Star {
	
	private Star star;
	
	public ProxyStar(Star star) {
		super();
		this.star = star;
	}

	@Override
	public void bookTicket() {
		System.out.println("ProxyStar.bookTicket()");
	}

	@Override
	public void collectMoney() {
		System.out.println("ProxyStar.collectMoney()");
	}

	@Override
	public void confer() {
		System.out.println("ProxyStar.confer()");
	}

	@Override
	public void signContract() {
		System.out.println("ProxyStar.signContract()");
	}

	@Override
	public void sing() {
		star.sing();
	}

}

4、client客户端

package cn.xiaomifeng1010.proxy.staticProxy;

public class Client {
	public static void main(String[] args) {
		Star real = new RealStar();
		Star proxy = new ProxyStar(real);
		
		proxy.confer();
		proxy.signContract();
		proxy.bookTicket();
		proxy.sing();
		
		proxy.collectMoney();
		
	}
}

2.7.5 动态代理:

动态代理(动态生成代理类)
• JDK自带的动态代理
• javaassist字节码操作库实现
• CGLIB
• ASM(底层使用指令,可维护性较差)

动态代理相比于静态代理的优点
– 抽象角色中(接口)声明的所以方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。

 

JDK自带的动态代理
– java.lang.reflect.Proxy
• 作用:动态生成代理类和对象
– java.lang.reflect.InvocationHandler(处理器接口)
• 可以通过invoke方法实现对真实角色的代理访问。
• 每次通过Proxy生成代理类对象对象时都要指定对应的处理器对象

Star realStar = new RealStar();
StarHandler handler = new StarHandler(realStar);
Star proxy = (Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new
Class[]{Star.class},handler);
proxy.sing();

动态代理实例演示:

package cn.xiaomifeng1010.proxy.dynamicProxy;

public interface Star {
	/**
	 * 面谈
	 */
	void confer();
	/**
	 * 签合同
	 */
	void signContract();
	/**
	 * 订票
	 */
	void bookTicket();
	/**
	 * 唱歌
	 */
	void sing();
	/**
	 * 收钱
	 */
	void collectMoney();
}
package cn.xiaomifeng1010.proxy.dynamicProxy;

public class RealStar implements Star {

	@Override
	public void bookTicket() {
		System.out.println("RealStar.bookTicket()");
	}

	@Override
	public void collectMoney() {
		System.out.println("RealStar.collectMoney()");
	}

	@Override
	public void confer() {
		System.out.println("RealStar.confer()");
	}

	@Override
	public void signContract() {
		System.out.println("RealStar.signContract()");
	}

	@Override
	public void sing() {
		System.out.println("RealStar(周杰伦本人).sing()");
	}
	
	
	
}
package cn.xiaomifeng1010.proxy.dynamicProxy;

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

public class StarHandler implements InvocationHandler {
	
	Star realStar;
	
	public StarHandler(Star realStar) {
		super();
		this.realStar = realStar;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		Object object = null;
		
		System.out.println("真正的方法执行前!");
		System.out.println("面谈,签合同,预付款,订机票");
		
		if(method.getName().equals("sing")){
			object = method.invoke(realStar, args);
		}
		
		System.out.println("真正的方法执行后!");
		System.out.println("收尾款");
		return object;
	}

}
package cn.xiaomifeng1010.proxy.dynamicProxy;

/**
 * 模拟动态生成的代理的结构
 * @author Administrator
 *
 */
public class ProxyStar implements Star {
	
	StarHandler handler;
	
	public ProxyStar(StarHandler handler) {
		super();
		this.handler = handler;
	}

	public void bookTicket() {
//		handler.invoke(this,当前方法 , args);
	}

	public void collectMoney() {
//		handler.invoke(this,当前方法 , args);
	}

	public void confer() {
//		handler.invoke(this,当前方法 , args);
	}

	public void signContract() {
//		handler.invoke(this,当前方法 , args);
	}

	public void sing() {
//		handler.invoke(this,当前方法 , args);
	}

}

客户端:

package cn.xiaomifeng1010.proxy.dynamicProxy;

import java.lang.reflect.Proxy;

public class Client {
	public static void main(String[] args) {
		
		Star realStar = new RealStar();
		StarHandler handler = new StarHandler(realStar);
		
		Star proxy = (Star) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), 
				new Class[]{Star.class}, handler);
		
		proxy.sing();
		
	}
	
}

动态代理关系类图:

 开发框架中应用场景:
– struts2中拦截器的实现
– 数据库连接池关闭处理
– Hibernate中延时加载的实现
– mybatis中实现拦截器插件
– AspectJ的实现
– spring中AOP的实现
• 日志拦截
• 声明式事务处理
– web service
– RMI远程方法调用
– ...
– 实际上,随便选择一个技术框架都会用到代理模式!!

面向切面编程(AOP)介绍

AOP(Aspect-Oriented Programming,面向切面的编程)
– 它是可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。它是一种新的方法论,它是对传统OOP编程的一种补充。

常用术语:
– 切面(Aspect):其实就是共有功能的实现。
– 通知(Advice):是切面的具体实现。
– 连接点(Joinpoint):就是程序在运行过程中能够插入切面的地点。
– 切入点(Pointcut):用于定义通知应该切入到哪些连接点上。
– 目标对象(Target):就是那些即将切入切面的对象,也就是那些被通知的对象
– 代理对象(Proxy):将通知应用到目标对象之后被动态创建的对象。
– 织入(Weaving):将切面应用到目标对象从而创建一个新的代理对象的过程。
• 开源的AOP框架
– AspectJ

2.8 桥接模式(Bridge Pattern)

2.8.1 桥接模式定义:

桥接模式也叫作桥梁模式,是一个比较简单的模式

定义:

将抽象和实现解耦,使得两者可以独立地变化。

桥接模式的重点是在“解耦”上,如何让它们两者解耦是我们要了解的重点。

桥接模式通用类图

 

Abstraction----抽象化角色

它的主要职责是定义出该角色的行为,同时保存一个对实现化角色的引用,该角色一般是抽象类。

Implementor----实现化角色

它是接口或者抽象类,定义角色必需的行为和属性。

RefinedAbstraction----修正抽象化角色

它引用实现化角色对抽象化角色进行修正。

ConcreteImplementor------具体实现化角色

它实现接口或者抽象类定义的方法和属性。

2.8.2 实际问题

不使用桥接模式演示:

package cn.xiaomifeng1010.bridge;

public interface Computer {
	void sale();
}

class Desktop implements Computer {
	@Override
	public void sale() {
		System.out.println("销售台式机!");
	}
}

class Laptop implements Computer {
	@Override
	public void sale() {
		System.out.println("销售笔记本!");
	}
}
class Pad implements Computer {
	@Override
	public void sale() {
		System.out.println("销售平板电脑!");
	}
}

class LenovoDesktop extends Desktop {
	@Override
	public void sale() {
		System.out.println("销售联想台式机");
	}
}
class LenovoLaptop extends Laptop {
	@Override
	public void sale() {
		System.out.println("销售联想笔记本");
	}
}
class LenovoPad extends Pad {
	@Override
	public void sale() {
		System.out.println("销售联想平板电脑");
	}
}



class ShenzhouDesktop extends Desktop {
	@Override
	public void sale() {
		System.out.println("销售神舟台式机");
	}
}
class ShenzhouLaptop extends Laptop {
	@Override
	public void sale() {
		System.out.println("销售神舟笔记本");
	}
}
class ShenzhouPad extends Pad {
	@Override
	public void sale() {
		System.out.println("销售神舟平板电脑");
	}
}


class DellDesktop extends Desktop {
	@Override
	public void sale() {
		System.out.println("销售戴尔台式机");
	}
}
class DellLaptop extends Laptop {
	@Override
	public void sale() {
		System.out.println("销售戴尔笔记本");
	}
}
class DellPad extends Pad {
	@Override
	public void sale() {
		System.out.println("销售戴尔平板电脑");
	}
}


 关系类图:

使用桥接模式演示:

 

package cn.xiaomifeng1010t.bridge;

/**
 * 品牌
 * @author Administrator
 *
 */
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("销售Dell电脑");
	}
	
}

class Shenzhou implements Brand {
	
	@Override
	public void sale() {
		System.out.println("销售神舟电脑");
	}
	
}
package cn.xiaomifeng1010.bridge;

/**
 * 电脑类型的维度
 * @author Administrator
 *
 */
public class Computer2 {
	
	protected Brand brand;
	
	public Computer2(Brand b) {
		this.brand = b;
	}
	
	public void sale(){
		brand.sale();
	}
	
}

class Desktop2 extends Computer2 {

	public Desktop2(Brand b) {
		super(b);
	}
	
	@Override
	public void sale() {
		super.sale();
		System.out.println("销售台式机");
	}
}

class Laptop2 extends Computer2 {
	
	public Laptop2(Brand b) {
		super(b);
	}
	
	@Override
	public void sale() {
		super.sale();
		System.out.println("销售笔记本");
	}
}
package cn.xiaomifeng1010.bridge;

public class Client {
	public static void main(String[] args) {
		//销售联想的笔记本电脑
		Computer2  c = new Laptop2(new Lenovo());
		c.sale();
		
		//销售神舟的台式机
		Computer2 c2 = new Desktop2(new Shenzhou());
		c2.sale();
		
		
	}
}

使用桥接模式类图:

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

桥接模式的优点:

抽象和实现分离:

这也是桥接模式的主要特点,它完全是为了解决继承的缺点而提出的设计模式,在该模式下,实现可以不受抽象的约束,不用再绑定在一个固定的抽象层次上。

优秀的扩充能力

实现细节对客户透明,客户不用关心细节的实现,它已经由抽象层通过聚合关系完成了封装。

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

 

 

 桥接模式实际开发中应用场景
– JDBC驱动程序
– AWT中的Peer架构
– 银行日志管理:
• 格式分类:操作日志、交易日志、异常日志
• 距离分类:本地记录日志、异地记录日志
– 人力资源系统中的奖金计算模块:
• 奖金分类:个人奖金、团体奖金、激励奖金。
• 部门分类:人事部门、销售部门、研发部门。
– OA系统中的消息处理:
• 业务类型:普通消息、加急消息、特急消息
• 发送消息方式:系统内消息、手机短信、邮件

2.9 组合模式(Composite Pattern)

2.9.1 组合模式的定义:

组合模式也叫合成模式,有时又叫做部分----整体模式(Part--Whole),主要是用来描述部分与整体的关系

定义:

将对象组合成树形结构以表示“部分----整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性

组合模式通用类图

Component---抽象构件角色

定义参加组合对象的共有方法和属性,可以定义一些默认的行为或属性。

Leaf-----叶子构件

叶子对象,其下再也没有其他的分支,也就是遍历的最小单位。

Composite----树枝构件(容器构件)

树枝对象,它的作用是组合树枝节点和叶子节点形成一个树形结构。

2.9.2 实例演示:

package cn.xiaomifeng1010.composite;

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

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


class ImageFile implements AbstractFile {
	private String name;
	
	public ImageFile(String name) {
		super();
		this.name = name;
	}

	@Override
	public void killVirus() {
		System.out.println("---图像文件:"+name+",进行查杀!");
	}
	
}
class TextFile implements AbstractFile {
	private String name;
	
	public TextFile(String name) {
		super();
		this.name = name;
	}
	
	@Override
	public void killVirus() {
		System.out.println("---文本文件:"+name+",进行查杀!");
	}
}
class VideoFile implements AbstractFile {
	private String name;
	
	public VideoFile(String name) {
		super();
		this.name = name;
	}
	
	@Override
	public void killVirus() {
		System.out.println("---视频文件:"+name+",进行查杀!");
	}
}

class Folder implements AbstractFile {
	private String name;
	//定义容器,用来存放本容器构建下的子节点
	private List<AbstractFile> list = new ArrayList<AbstractFile>();
	
	public Folder(String name) {
		super();
		this.name = name;
	}

	public void add(AbstractFile file){
		list.add(file);
	}
	public void remove(AbstractFile file){
		list.remove(file);
	}
	public AbstractFile getChild(int index){
		return list.get(index);
	}

	@Override
	public void killVirus() {
		System.out.println("---文件夹:"+name+",进行查杀");
		
		for (AbstractFile file : list) {
			file.killVirus();
		}
		
	}
	
}

package cn.xiaomifeng1010.composite;

/**
 * 抽象组件
 * @author Administrator
 *
 */
public interface Component {
	void operation();
}

//叶子组件
interface Leaf extends Component {
}
//容器组件
interface Composite extends Component {
	void add(Component c);
	void remove(Component c);
	Component getChild(int index);
}
package cn.xiaomifeng1010.composite;

public class Client {
	public static void main(String[] args) {
		AbstractFile f2,f3,f4,f5;
		Folder f1 = new Folder("我的收藏");
		
		f2 = new ImageFile("老高的大头像.jpg");
		f3 = new TextFile("Hello.txt");
		f1.add(f2);
		f1.add(f3);
		
		Folder f11 = new Folder("电影");
		f4 = new VideoFile("笑傲江湖.avi");
		f5 = new VideoFile("神雕侠侣.avi");
		f11.add(f4);
		f11.add(f5);
		f1.add(f11);
		
		
//		f2.killVirus();
		
		f1.killVirus();
		
		
	}
}

关系类图:

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

开发中的应用场景:
– 操作系统的资源管理器
– GUI中的容器层次图
– XML文件解析
– OA系统中,组织结构的处理
– Junit单元测试框架
• 底层设计就是典型的组合模式,TestCase(叶子)、TestUnite(容器)、Test接口(抽象)

 

Junit单元测试框架底层设计
– 底层设计就是典型的组合模式,TestCase(叶子)、TestUnite(容器)、Test接口(抽象)

2.9.3 组合模式的优点

高层模块调用简单

一颗树形结构中的所有节点都是Component,局部和整体对调用者来说没有任何区别,也就是说,高层模块不必关心自己处理的是单个对象还是整个组合结构,简化了高层模块的代码。

节点自由增加

使用了组合模式后,我们可以看看,如果想增加一个树枝节点、树叶节点是不是很容易,只要找到它的父节点就成,非常容易扩展,符合开闭原则,对以后的维护非常有利。

组合模式的缺点:

组合模式有一个非常明显的缺点,看到我们在场景中的定义,提到的树叶和树枝使用时的定义了吗?直接使用了实现类!这在面向接口编程上是很不恰当的,与依赖倒置原则冲突。

2.10 装饰模式(Decorator Pattern)

2.10.1 装饰模式的定义

动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。

                                                    装饰模式通用类图

Component---抽象构件

Component是一个接口或者抽象类,就是定义我们最核心的对象,也是最原始的对象。

注意:在装饰模式中,必须有一个最基本、最核心、最原始的接口或者抽象类充当Component抽象构件。

ConcreteComponent----具体构件

ConcreteComponent是最核心、最原始、最基本的接口或者抽象类的实现,你要装饰的就是它。

Decorator----装饰角色

一般是一个抽象类,做什么用呢?实现接口或者抽象方法,它里面可不一定有抽象方法呀,在它的属性里必然有一个private变量指向Component抽象构件。

ConcreteDecorator----具体装饰角色

ConcreteDecoratorA和ConcreteDecoratorB是两个具体的装饰类,你要把你最核心、最原始的、最基本的东西装饰成其他东西。

 

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

2.10.2 具体实例演示

package cn.xiaomifeng1010.decorator;

/**
 * 抽象构建
 * @author Administrator
 *
 */
public interface ICar {
	void move();
}

//ConcreteComponent 具体构件角色(真实对象)
class Car implements ICar {
	@Override
	public void move() {
		System.out.println("陆地上跑!");
	}
}

//Decorator装饰角色
class SuperCar implements ICar {
	protected ICar car;
	public SuperCar(ICar car) {
		super();
		this.car = car;
	}

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

//ConcreteDecorator具体装饰角色
class FlyCar extends SuperCar {

	public FlyCar(ICar car) {
		super(car);
	}
	
	public void fly(){
		System.out.println("天上飞!");
	}

	@Override
	public void move() {
		super.move();
		fly();
	}
	
}

//ConcreteDecorator具体装饰角色
class WaterCar extends SuperCar {
	
	public WaterCar(ICar car) {
		super(car);
	}
	
	public void swim(){
		System.out.println("水上游!");
	}
	
	@Override
	public void move() {
		super.move();
		swim();
	}
	
}

//ConcreteDecorator具体装饰角色
class AICar extends SuperCar {
	
	public AICar(ICar car) {
		super(car);
	}
	
	public void autoMove(){
		System.out.println("自动跑!");
	}
	
	@Override
	public void move() {
		super.move();
		autoMove();
	}
	
}



package cn.xiaomifeng1010.decorator;

import java.io.FileInputStream;
import java.io.InputStreamReader;

public class Client {
	public static void main(String[] args) {
		Car car  = new Car();
		car.move();
		
		System.out.println("增加新的功能,飞行----------");
		FlyCar flycar = new FlyCar(car);
		flycar.move();
		
		System.out.println("增加新的功能,水里游---------");
		WaterCar  waterCar = new WaterCar(car);
		waterCar.move();
		
		System.out.println("增加两个新的功能,飞行,水里游-------");
		WaterCar waterCar2 = new WaterCar(new FlyCar(car));
		waterCar2.move();
		
		
//		Reader r = new BufferedReader(new InputStreamReader(new FileInputStream(new File("d:/a.txt"))));
		
	}
}

关系类图:

2.10.3 开发中使用的场景:
– IO中输入流和输出流的设计
– Swing包中图形界面构件功能
– Servlet API 中提供了一个request对象的Decorator设计模式的默认实
现类HttpServletRequestWrapper,HttpServletRequestWrapper
类,增强了request对象的功能。
– Struts2中,request,response,session对象的处理

 

IO流实现细节:
– Component抽象构件角色:
• io流中的InputStream、OutputStream、Reader、Writer
– ConcreteComponent 具体构件角色:
• io流中的FileInputStream、FileOutputStream
– Decorator装饰角色:
• 持有一个抽象构件的引用:io流中的FilterInputStream、FilterOutputStream
– ConcreteDecorator具体装饰角色:
• 负责给构件对象增加新的责任。Io流中的BufferedOutputStream、BufferedInputStream等。

 

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


• 优点
– 扩展对象功能,比继承灵活,不会导致类个数急剧增加
– 可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更加强大的对象
– 具体构建类和具体装饰类可以独立变化,用户可以根据需要自己增加新的具体构件子类和具体装饰子类。
• 缺点
– 产生很多小对象。大量小对象占据内存,一定程度上影响性能。
– 装饰模式易于出错,调试排查比较麻烦。

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

 

2.11 外观模式(Facade Pattern)

2.11.1 外观模式定义:

外观模式也叫门面模式,要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。外观模式提供一个高层次的接口,使得子系统更易于使用。

外观模式注重“统一的对象”,也就是提供一个访问子系统的接口,除了这个接口不允许有任何访问子系统的行为发生。 

外观模式通用类图

类图看起来比较简单,但是它代表的意义可是异常复杂,Subsystem Classes是子系统所有类的简称,它可能代表一个类,也可能代表几十个对象的集合,甭管多少对象,我们把这些对象全部圈入子系统的范畴,结构图如下

外观模式示意图

 

Facade---外观角色

客户端可以调用这个角色的方法。此角色知晓子系统的所有功能和责任。一般情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去,也就说该角色没有实际的业务逻辑,只是一个委托类。

Subsystem----子系统角色

可以同时有一个或者多个子系统。每一个子系统都不是一个单独的类,而是一个类的集合。子系统并不知道外观的存在,对于子系统而言,外观仅仅是另外一个客户端而已。

 

2.11.2 具体实例演示:

不使用外观模式 

package cn.xiaomifeng1010.facade;

public interface 工商局  {
	void checkName();  //核名
}

class 海淀区工商局 implements 工商局 {

	@Override
	public void checkName() {
		System.out.println("检查名字是否有冲突!");
	}

}
package cn.xiaomifeng1010.facade;

public interface 税务局 {
	void taxCertificate();  //办理税务登记证
}


class 海淀税务局 implements 税务局 {

	@Override
	public void taxCertificate() {
		System.out.println("在海淀税务局办理税务登记证!");
	}

}
package cn.xiaomifeng1010.facade;

public interface 银行 {
	void  openAccount();  //开户
}


class 中国工商银行 implements 银行 {

	@Override
	public void openAccount() {
		System.out.println("在中国工商银行开户!");
	}

}

 

package cn.xiaomifeng1010.facade;

public class Client1 {
	public static void main(String[] args) {
		工商局  a = new 海淀区工商局();
	a.checkName();
		质检局 b = new 海淀质检局();
	b.orgCodeCertificate();
		税务局  c  = new 海淀税务局();
	c.taxCertificate();
	银行  d = new 中国工商银行();
		d.openAccount();
		
		
		
	}
}

不使用外观模式类图:

 使用外观模式:

package cn.xiaomifeng1010.facade;

/**
 * 办理注册公司流程的门面对象
 * @author Administrator
 *
 */
public class RegisterFacade {
	public void register(){
		工商局  a = new 海淀区工商局();
		a.checkName();
		质检局 b = new 海淀质检局();
		b.orgCodeCertificate();
		税务局  c  = new 海淀税务局();
		c.taxCertificate();
		银行  d = new 中国工商银行();
		d.openAccount();
	}
}

 

package cn.xiaomifeng1010.facade;

public class Client1 {
	public static void main(String[] args) {
//		工商局  a = new 海淀区工商局();
//		a.checkName();
//		质检局 b = new 海淀质检局();
//		b.orgCodeCertificate();
//		税务局  c  = new 海淀税务局();
//		c.taxCertificate();
//		银行  d = new 中国工商银行();
//		d.openAccount();
		
		new RegisterFacade().register();
		
	}
}

使用外观模式类图:

2.11.3 开发中常见的场景
– 频率很高。哪里都会遇到。各种技术和框架中,都有外观模式的使用。如:
• JDBC封装后的,commons提供的DBUtils类,Hibernate提供的工具类、Spring JDBC工具类等 

2.11.4 外观模式的优点

减少系统的相互依赖

提高了灵活性

提高安全性

缺点:

外观模式最大的缺点就是不符合开闭原则(对修改关闭,对扩展开放)。

2.12 享元模式(Flyweight Pattern)

2.12.1 享元模式的定义:

使用共享对象可有效地支持大量的细粒度的对象重用。

享元模式定义为我们提出了两个要求:细粒度的对象和共享对象。我们知道分配太多的对象到应用程序中将有损程序的性能,同时还容易造成内存溢出,那怎么避免呢?我们先来了解一下对象的内部状态和外部状态。

 要求细粒度对象,那么不可避免地使得对象数量多且性质相近,那我们就将这些对象的信息分为两个部分:内部状态(intrinsic)与外部状态(extrinsic)。

• 内部状态:可以共享,不会随环境变化而改变
• 外部状态:不可以共享,会随环境变化而改变

享元模式通用类图

 

享元模式实现:
– FlyweightFactory享元工厂类
• 创建并管理享元对象,享元池一般设计成键值对
– FlyWeight抽象享元类
• 通常是一个接口或抽象类,声明公共方法,这些方法可以向外界提供对象的内部状态,设置外部状态。
– ConcreteFlyWeight具体享元类
• 为内部状态提供成员变量进行存储
– UnsharedConcreteFlyWeight非共享享元类
• 不能被共享的子类可以设计为非共享享元类

2.12.2 具体实例演示:

以围棋为例

颜色、形状、大小等属性可以共享,内部状态

位置不可共享,外部状态

package cn.xiaomifeng1010.flyweight;

/**
 * 享元类
 * @author Administrator
 *
 */
public interface ChessFlyWeight {
	void setColor(String c);
	String getColor();
	void display(Coordinate c);
}


class ConcreteChess implements ChessFlyWeight {

	private String color;
	
	public ConcreteChess(String color) {
		super();
		this.color = color;
	}

	@Override
	public void display(Coordinate c) {
		System.out.println("棋子颜色:"+color);
		System.out.println("棋子位置:"+c.getX()+"----"+c.getY());
	}

	@Override
	public String getColor() {
		return color;
	}

	@Override
	public void setColor(String c) {
		this.color = c;
	}
	
}

 

package cn.xiaomifeng1010.flyweight;

/**
 * 外部状态UnSharedConcreteFlyWeight
 * @author Administrator
 *
 */
public class Coordinate {
	private int x,y;

	public Coordinate(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}
	
}
package cn.xiaomifeng1010.flyweight;

import java.util.HashMap;
import java.util.Map;

/**
 * 享元工厂类
 * @author Administrator
 *
 */
public class ChessFlyWeightFactory {
	//享元池
	private static Map<String,ChessFlyWeight> map = new HashMap<String, ChessFlyWeight>();
	
	public static ChessFlyWeight  getChess(String color){
		
		if(map.get(color)!=null){
			return map.get(color);
		}else{
			ChessFlyWeight cfw = new ConcreteChess(color);
			map.put(color, cfw);
			return cfw;
		}
		
	}
	
	
}
package cn.xiaomifeng1010.flyweight;

public class Client {
	public static void main(String[] args) {
		ChessFlyWeight chess1 = ChessFlyWeightFactory.getChess("黑色");
		ChessFlyWeight chess2 = ChessFlyWeightFactory.getChess("黑色");
		System.out.println(chess1);
		System.out.println(chess2);
		
		System.out.println("增加外部状态的处理===========");
		chess1.display(new Coordinate(10, 10));
		chess2.display(new Coordinate(20, 20));
		
		
	}
}

关系类图:

 2.12.3 享元模式的优缺点:

优点
– 极大减少内存中对象的数量
– 相同或相似对象内存中只存一份,极大的节约资源,提高系统性能
– 外部状态相对独立,不影响内部状态
• 缺点
– 模式较复杂,使程序逻辑复杂化
– 为了节省内存,共享了内部状态,分离出外部状态,而读取外部状态使运行时间变长。用时间换取了空间。

2.13 结构型模式小结:

结构型模式汇总
模式作用
代理模式为真实对象提供一个代理,从而控制对真实对象的访问
适配器模式使原本由于接口不兼容不能一起工作的类可以一起工作
桥接模式处理多层继承结构,处理多维度变化的场景,将各个维度设计成独立的继承结构,使各个维度可以独立的扩展在抽象层建立关联。
组合模式将对象组合成树状结构以表示”部分和整体”层次结构,使得客户可以统一的调用叶子对象和容器对象
装饰模式动态地给一个对象添加额外的功能,比继承灵活
外观模式为子系统提供统一的调用接口,使得子系统更加容易使用
享元模式运用共享技术有效的实现管理大量细粒度对象,节省内存,提高效率

第二部分告一段落

本文部分内容引用自《设计模式之禅第二版》作者:秦小波

第三部分:GOF23种设计模式在Java中的应用(part 3)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值