享元模式

享元模式的目的在于运用共享技术,使得一些细粒度的对象可以共享。
多食用细粒度的对象,以便于重用或者重构。

定义

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

大量的细粒度对象,需要把对象的共有属性提取出来,做为内部状态,剩余的非共有做为外部状态。
内部状态:可以共享出来的信息,存储在襄垣对象内部,并且不会随着环境的改变而改变。
外部状态:对象得以依赖的一个标记,是随着环境的改变而改变的,不可以共享的状态。

享元模式通用类图

实现

Flyweight

抽象享元角色,产品抽象类,同时定义出对象的外部状态和内部状态的接口或者实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public abstract  class Flyweight{
private String instrinsic; //inner status
protected final String extrinsic; //external status

public Flyweight(String extrinsic){
this.extrinsic = extrinsic;
}

public abstract void operate();

public String getInstrinsic(){
return this.instrinsic;
}

public void setInstrinsic(String instrinsic){
this.instrinsic = instrinsic;
}
}

ConcreteFlyweight

具体享元角色,实现抽象角色的定义业务,该角色中需要注意的是内部状态处理应该与环境无关。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ConcreteFlyweight1 extends Flyweight{

public ConcreteFlyweight1(String _Extrinsic){
super(_Extrinsic);
}

public void operate(){
//...
}
}

public class ConcreteFlyweight2 extends Flyweight{

public ConcreteFlyweight2(String _Extrinsic){
super(_Extrinsic);
}

public void operate(){
//...
}
}

unsharConcreteFlyweight

不可共享的享元角色

不存在外部状态或者安全要求不能够使用共享技术的对象,该对象一般不会出现在享元工厂中。

FlyweightFactory

享元工厂,构造一个池容器,同时提供从池中获得对象的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;
}
}

应用

优点

可以减少应用程序创建的对象,降低程序内存的占用,增强程序的性能。

缺点

提高了程序的复杂性,需要分离出内部状态和外部状态,而且外部状态具有固话特征,不能随着内部状态的改变而改变。

扩展

  • 线程安全的问题
    创建的对象尽量多,多到满足业务对象为止。
  • 性能平衡
    外部状态最好以基本类型为标志,如String, Int等可以大幅提升效率。

门面模式

门面模式也叫外观模式。

定义

要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。

外观模式提供一个高层次的接口,使得子系统更易于使用。

门面模式注重要求“统一的对象”,提供一个访问子系统的接口,除了这个接口不允许任何访问子系统的行为发生。

外观模式

实现

Subsystem

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class classA{
public void doSomethingA(){
//..logic a
}
}

public class classB{
public void doSomethingB(){
//. logic b
}
}

public class classC{
public void doSomethingC(){
//. logic c.
}
}

Facade

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Facade{
private classA a = new classA();
private classB b = new classB();
private classC c = new classC();

public void methodA(){
this.a.doSomethingA();
}

public void methodB(){
this.b.doSomethingB();
}

public void methodC(){
this.c.doSomethingC();
}
}

应用

优点

  • 减少系统的相互依赖
  • 提高了灵活性
  • 提高安全性

使用场景

  • 为一个复杂的模块或者子系统提供一个供外界访问的接口
  • 子系统相对独立
  • 预防低水平人员带来的风险扩散
  • 一个子系统可以有多个门面
  • 门面不参与子系统内的业务逻辑

适配器模式

当我们去国外旅行时,我们国内的插头不能直接插入到国外的插线板上,我们需要购买一个电源适配器来适配国外的插线板。

或者最新版的macbook pro 用的是utypec,并不能直接插入usb3.0的u盘,我们需要购买一个适配器做转换。

定义

将一个类的接口编程客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。

适配器模式通用类图

实现

Target

目标角色是一个接口或者抽象类,是一个已经存在的角色,不可能去修改角色中的方法。

1
2
3
public interface Target{
public void request();
}

ConcreteTarget

具体的目标角色实现类。

1
2
3
4
5
public class ConcreteTarget implements Target{
public void request(){
//...
}
}

Adaptee

源角色,已经使用中的角色。需要将Adaptee转换为Target。

1
2
3
4
5
public class Adaptee{
public void doSomething(){
//...
}
}

Adaptor

适配器角色。

1
2
3
4
5
public class Adaptor extends Adaptee implements Target{
public void request(){
super.doSomething();
}
}

Use

1
2
3
4
5
6

Target target = new ConcreteTarget();
target.request();

Target target2 = new Adaptor();
target2.request();

扩展

当需要把多个Adaptee转换为Target的时候,就不能使用继承了,因为Java中不支持多继承。
这种方式叫做对象适配器。
我们可以使用聚合(关联)来完成适配。

对象适配器类图

类适配器是由类的继承关系完成的。
对象适配器是由类的关联关系纪念性耦合。

桥接模式

桥梁模式的重点在解耦。

定义

将抽象和实现解耦,使得两者可以独立的变化。
它使用了类间的聚合关系、继承、覆写等常用功能。解决了继承的缺点而提出的设计模式。

桥接模式通用类图

实现

抽象角色引用实现角色,或者说抽象角色的部分实现是由实现角色完成的。

Abstraction

抽象化角色,定义出该抽象角色的行为,同时保存一个对实现化角色的引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public abstract class Abstraction{

private Implementor imp;
//必须指定实现者
public Abstraction(Implementor imp){
this.imp = imp;
}

public void request(){
this.imp.doSomething();
}
public Implementor getImp(){
return imp;
}
}

RefindAbstraction

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

1
2
3
4
5
6
7
8
9
10
11
12
13
public class RefindAbstraction extends Abstraction{

public RefindAbstraction(Implementor imp){
super(imp);
}

@Override
public void request(){
//....
super.request();
super.getImp().doAnyThing();
}
}

Implementor

实现化角色,接口或者抽象类,定义角色必须的行为和属性。

1
2
3
4
public interface Implementor{
public void doSomething();
public void doAnyThing();
}

ConcreteImplementor

具体实现化角色,实现接口或者抽象类定义的方法和属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ConcreteImplementor1 implements Implementor{

public void doSomething(){
//
}
public void doAnyThing(){
//
}
}

public class ConcreteImplementor2 implements Implementor{

public void doSomething(){
//
}
public void doAnyThing(){
//
}
}

Use

1
2
3
Implementor imp = new ConcreteImplementor1();
Abstraction abs = new RefindAbstraction(imp);
abs.request();

应用

优点

  • 抽象和实现分离
    为了解决继承的缺点而提出的设计模式,在该模式下,实现可以不受抽象的约束,不再绑定到固定的抽象层次上。

  • 优秀的扩展能力

可以随意增加抽象和实现。

  • 实现细节对客户透明

由抽象层通过聚合关系完成了封装。

使用场景

  • 不希望或者不适用使用继承的场景
    继承层次太高,无法更细化设计颗粒等场景,需要考虑使用。
  • 接口或者抽象类不稳定的场景
    接口或者抽象类不稳定,经常变更时,使用桥梁模式。
  • 重用型要求高的场景
    设计的颗粒度粤西,被重用的可能性越大。
    采用继承受到父类的限制,不可能出现太细的粒度。

装饰器模式

定义

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

装饰器模式通用类图

实现

Component

Component 是一个接口或者抽象类,定义需要被装饰的原始类。

1
2
3
public abstract class Component{
public abstract void operate();
}

Concrete Component

Component 的实现类。

1
2
3
4
5
6
7
8
public class ConcreteComponent extends Component{

@Override
public void operate(){
//....
}

}

Decorator

抽象类,用于实现接口或者抽象方法。包含被修饰的Componnet属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
public abstract class Decorator extends Component{

private Component component = null;

public Decorator(Component c){
this.component = c;
}
@Override
public void operate(){
this.component.operate();
}

}

Concrete Decorator

具体装饰角色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class ConcreteDecorator1 extends Decorator{
public ConcreteDecorator1(Component component){
super(component);
}
private void method1(){
//....
}
public void operate(){
this.method1();
super.operate();
}
}


public class ConcreteDecorator2 extends Decorator{
public ConcreteDecorator2(Component component){
super(component);
}
private void method2(){
//....
}
public void operate(){
super.operate();
this.method2();
}
}

Use

1
2
3
4
Component c = new ConcreteComponent();
c = new ConcreteDecorator1(c);
c = new ConcreteDecorator2(c);
c.operate();

应用

优点

装饰类和被装饰类可以独立发展,而不会相互耦合。
装饰模式是继承关系的一个替代方案。
装饰模式可以动态地扩展一个实现类的功能。

缺点

当装饰多层时导致系统复杂度高。

组合模式

也叫合成模式或者部分整体模式,主要用来描述部分与整体的关系。

定义

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

组合模式的通用类图

Component

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public abstract class Component{
private Component parent = null;
private ArrayList<Component> list = new ArrayList<>();
public void add(Component c){
c.parent = this;
this.list.add(c);
}

public void remove(Component c){
this.list.remove(c);
}

public List<Component> getChildren(){
return this.list;
}

public Component getParent(){
return this.parent;
}
}

Composite

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

1
2
3
public class Composite extends Component{

}

Leaf

叶子对象,下面没有分支,最小的遍历单位。

1
2
3
public class Leaf extends Component{

}

应用

优点

  • 高层模块调用简单
    一颗属性结构中的所有节点都是Component,局部和整体对调用者来说没有任何区别,也就是说高层模块不必关心自己处理的是单个对象还是整个组合结构,简化了高层模块的代码。
  • 节点自由度增加
    使用组合模式后,如果想增加一个树枝节点、树叶节点都非常容易。
    如何开闭原则,利于维护。

缺点

(安全模式)直接使用了实现类(可以扩展)。与依赖倒置原则冲突,限制了接口的影响范围。

扩展

透明的组合模式

以上的例子就是透明的组合模式,吧用来组合使用的方法放到抽象类中,无论叶子对象还是树枝对象都有相同的结构,通过getChildren();判断当前节点是否为叶子节点还是根节点。

透明模式的好处是遵循了依赖倒置原则

安全的组合模式

安全模式是将叶子节点和树枝节点彻底分开,树枝节点单独拥有用来组合的方法。

Component

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

1
2
3
4
5
6
public abstract class Component{
private Component parent = null;
public Component getParent(){
return this.parent;
}
}
Composite

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Composite extends Component{
private ArrayList<Component> list = new ArrayList<>();
public void add(Component c){
c.parent = this;
this.list.add(c);
}

public void remove(Component c){
this.list.remove(c);
}

public List<Component> getChildren(){
return this.list;
}
}
Leaf

叶子对象,下面没有分支,最小的遍历单位。

1
2
3
public class Leaf extends Component{

}

代理模式

代理模式也叫委托模式,许多其他的模式如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了代理模式。
在日常应用中,代理模式可以提供非常好的访问控制。

定义

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

比如,我们的DB的访问权限不能直接公开,一般会公开一个接口来提供对这些数据。

代理模式通用类图

实现

抽象主题Subject

可以是抽象类或者接口,普通的业务类型定义。

1
2
3
public interface Subject{
public void request();
}

具体主题RealSubject

委托角色或者被代理角色。业务逻辑的具体执行者。

1
2
3
4
5
public class RealSubject implements Subject{
public void request(){
//do something.
}
}

代理主题Proxy

代理类、委托类, 负责对真实角色的调用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Proxy implements Subject{
private Subject subject = null;

public Proxy(){
this.subject = new Proxy();
}

public Proxy(Subject subject){
this.subject = subject;
}

public void request(){
this.before();
this.subject.request();
this.after();
}

private void before(){
//before
}
private void after(){
//after.
}
}

应用

优点

  • 职责清晰
    真实的角色就是实现实际的业务逻辑,不用关心其他非本职责的事物,通过后期的代理完成一件事物,附带的结果就是编程简洁清晰。
  • 扩展性好
    具体主题角色是随时都会发生变化的,只要它实现了接口,代理类可以在不做任何修改的情况下使用。
  • 智能化
    用于动态代理。

扩展

普通代理

客户端只能访问代理角色,而不能访问真实角色。屏蔽了真实角色的变更对高层模块的影响。该模式适合对扩展性要求较高的场合。一般通过编程规范类约束来禁止new一个真实的角色。

这种方式调用者只需要知道代理即可, 不需要知道代理了谁。

普通代理的实现者:

1
2
3
4
5
6
7
8
9
10
11
12
public class GamePlayer implements IGamePlayer{
private String name = "";
public GamePlayer(IGamePlayer _gamePlayer, String _name)throws Exception{
if(_gamePlayer == null || !(_gamePlayer instanceof GameProxy)){
throw new Exception("Create Not Allowed!");
}
this.name = _name;
}
public void killMonster(){
//do...
}
}

普通代理的代理者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class GamePlayerProxy implements IGamePlayer{
private IGamePlayer gamePlayer = null;
public GamePlayerProxy(String name){
try{
gamePlayer = new GamePlayer(this,name);
}catch(Exception e){
//TODO
}
}

public void killMonster(){
this.gamePlayer.killMonster();
}
}

强制代理

只有通过真实角色指定的代理类才可以访问。

1
2
3
4
public interface IGamePlayer{
void killMonster();
public IGamePlayer getProxy();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class GamePlayer implements IGamePlayer{
private String name = "";
private IGamePlayer proxy = null;
public GamePlayer(String _name){
this.name = _name;
this.proxy = new GamepPlayerProxy(this);
}
public IGamePlayer getProxy(){
return this.proxy;
}
public void killMonster(){
//do...
}
}
1
2
3
4
5
6
7
8
9
public class GamePlayerProxy implements IGamePlayer{
private IGamePlayer gamePlayer = null;
public GamePlayerProxy(IGamePlayer _gamePlayer){
this.gamePlayer = _gamePlayer;
}
public void killMonster(){
this.gamePlayer.killMonster();
}
}

动态代理

在实现阶段不需要关心代理谁, 在运行阶段才指定代理哪个对象。

通过InvocationHandler接口,所有方法都由该Handler来接管实际的业务处理。

在不改变已有代码结构的情况下增强或者控制对象的行为。

动态代理Handler类:

1
2
3
4
5
6
7
8
9
public class MyInvocationHandler implements InvocationHandler{
private Object target = null;
public MyInvocationHandler(Object _obj){
this.target = _obj;
}
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable{
return method.invoke(this.target, args);
}
}

通知接口与实现

1
2
3
4
5
6
7
8
public interface IAdvice{
public void exec();
}
public class BeforeAdvice implements IAdvice{
public void exec(){
//....
}
}

动态代理类:

1
2
3
4
5
6
7
8
public class DynamicProxy<T>{
public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h){
if(condition){ //在condition连接点执行BeforeAdvice通知。
(new BeforeAdvice()).exec();
}
return (T)Proxy.newProxyInstance(loader, interface, h);
}
}

Client:

1
2
3
4
Subject subject = new RealSubject();
InvocationHandler handler = new MyInvocationHandler(subject);
Subject proxy = DynamicProxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), handler);
proxy.killMonster();

扩展

  • CGLib
1
2
3
4
5
6
7
8
9
10
public class LogInterceptor implements MethodInterceptor {

public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

System.out.println("Before Interceptor1");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("After Interceptor1");
return result;
}
}
1
2
3
4
5
6
7
8
9
10
public class LogInterceptor2 implements MethodInterceptor {

public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

System.out.println("Before Interceptor2");
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("After Interceptor2");
return result;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class LogCallBackFilter implements CallbackFilter {

public int accept(Method method) {
if ("upgrade".equals(method.getName())){
System.out.println("Upgrade Filter");
return 0;
}

System.out.println("Kill Monster Filter");
return 1;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
LogInterceptor logInterceptor = new LogInterceptor();

LogInterceptor2 logInterceptor2 = new LogInterceptor2();

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(GamePlayer.class);
enhancer.setCallbacks(new Callback[]{logInterceptor,logInterceptor2, NoOp.INSTANCE});

enhancer.setCallbackFilter(new LogCallBackFilter());

IGamePlayer gamePlayer = (IGamePlayer)enhancer.create(new Class[]{String.class}, new Object[]{"Lisi"});

gamePlayer.killMonster();
gamePlayer.upgrade();
  • ASM
  • AOP

扩展阅读
cglib动态代理、asm学习笔记
Java动态代理详解