Java设计模式学习笔记之职责型模式

一、职责型模式介绍:

 1.普通的对象需要一些独立操作的信息和方法。然而,有时却需要将对象从一般的独立性操作中分离出来,以便集中职责。

很多设计模式都能满足这一需求。有的模式则通过引入对象来封装这些请求,并将该对象从依赖于它的其他对象中分离出来。

面向职责的模式提供了用于集中、加强以及限制普通对象责任的技术。

 2.常规的职责型模式:

 一个易于使用的类的特征在于,它的方法名是有意义的,并能准确地表述方法要做的事情。

在面向对象的系统中,职责的合理分配所建立的原则,似乎促进了计算机科学更进一步的成熟。在一个系统中,如果每个类

和方法都清晰定义了它的职责,并能正确使用它们,这个系统就是迄今为止所能见到最为健壮的系统。

 3.根据可见性控制职责:

 类和方法承担着各种各样的职责,Java可以限制类、字段和方法的可见性,从而去限制其他开发人员对开发的代码的

调用。

 4.超越普通职责:

 无论一个类如何限制它的成员,面向对象开发通常都会将职责分散到各个独立的对象中。换句话说,面向对象开发促进

了封装,封装是指对象基于自己的数据进行操作。

 职责分离是一种规范的做法,但一些设计模式却反对这种规范,并且将职责转移到中间对象或者中心对象。

 单例模式:将职责集中到某个类的单个实例中;

观察者模式:将对象从依赖于它的对象中解耦;

调停者模式:将职责集中在某个类,该类可以监督其他对象的交互;

代理模式:让一个对象扮演其他对象的行为;

职责链模式:允许将请求传递给职责链的其他对象,直到这个请求被某个对象处理;

享元模式:将共享的、细粒度的对象职责集中管理。

二、单例(Singleton)模式:

 通常,对象通过在自身属性上执行任务来承担自己的职责,除了需要维护自身的一致性外,无须承担其他任何职责。

在某些场景,需要找到承担职责的对象,并且这个对象是它所属类的唯一实例。这样就可以使用单例模式。

单例模式的意图是为了确保一个类有且仅有一个实例,并为它提供一个全局访问点。

 1.单例模式机制:

为了避免多个实例化该类,可以创建唯一一个构造函数,并将其设置为私有的访问权限。注意,如果创建了其他非私有的构造

函数,或者没有创建任何构造函数,其他对象都能够实例化该类。

 创建一个担当着独一无二角色的对象,有多种方式。但是,不管你如何创建一个单例对象,都必须确保其他开发人员不能

创建该单例对象的新的实例。

 2.单例模式的写法:

 第一种写法(懒汉,线程不安全):这种写法lazy loading很明显,但是致命的是在多线程不能正常工作

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
  
    public static Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}                                                                                                         


    第二种写法(懒汉,线程安全):在多线程中能很好的工作,但是效率低,99%情况下不需要同步:

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}  

   第三种写法(饿汉):基于classloader机制避免了多线程的同步问题,在instance在类装载时就实例化,虽然导致类

装载的原因有很多,在单例模式中大多数都是调用getInstance方法,但是也不能有其他的方式(或者其他的静态方法)导致装

载,这时初始化instance没有达到lazy loading的效果。

public class Singleton {                                                                                         
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return instance;  
    }  
}  

 第四种写法(饿汉,变种):在类初始化即实例化instance

public class Singleton {                                                                                       
    private Singleton instance = null;  
    static {  
    instance = new Singleton();  
    }  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return this.instance;  
    }  
}  

 第五种写法(静态内部类):利用了classloader的机制来保证初始化instance时只有一个线程,跟第三和第四不同是:

第三和第四是只要Singleton类被装载了,那么instance就会被实例化,而这种方式是Singleton不一定被初始化。因为

SingletonHolder类没有被主动使用,只有现实通过调用getInstance方法时,才会现实装载SingletonHolder类,从而实例化。 

public class Singleton {  
    private static class SingletonHolder {  
    private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
    return SingletonHolder.INSTANCE;  
    }  
}  

 第六种写法(枚举):这种方式是Effective Java作者提倡的方式,它不仅避免多线程同步问题,而且还能防止反序列

化重新创建新的对象,jdk1.5才加入enum特性 

public enum Singleton {                                                                                                 
    INSTANCE;  
    public void whateverMethod() {   
    }  
} 

 第七种写法(双重校验锁):

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
} 
 3.单例和线程:

 在多线程环境下延迟初始化一个单例模型,必须避免多个线程同时初始化该单例对象。在多线程环境下,无法保证在

其他线程开始执行该方法时,当前线程完整地执行完该方法。这可能出现两个线程同时初始化一个单例对象的情况。

 4.小结:

 单例模式保证类仅有一个实例,并为其提供了一个全局访问点。通过延迟初始化(仅在第一次使用时才初始化),一个

单例对象是达到此目的的通用做法。对象具有唯一性,并不意味着使用了单例模式。单例模式通过隐藏构造函数,提供对象创建

的唯一入口点,从而将类的职责集中在类的单个实例中。


三、观察者(Observer)模式:

 客户端常用调用它所感兴趣的对象的方法来获取信息。然而,一旦感兴趣的对象发生改变就会出现问题:客户端怎样才能直到

它所依赖信息发生了改变?

 当客户端感兴趣的对象某个方面发生了改变时,需要创建一个对象负责通知客户端。问题是,由于客户端自己知道它所感兴趣

的某些方面,因此这个对象自身却不应该承担更新客户端的职责。一个解决方案是当对象发生改变时通知客户端,让客户端自己去

查询对象的新状态。


 观察者模式的意图是在多个对象之间定义一对多的依赖关系,当一个对象的状态发生改变时,会通知依赖于它的对象,并根据

新状态做出相应的反应。

 1.观察者模式中的角色:

抽象被观察者角色:把所有的对观察者对象的引用保存在一个集合中,每个被观察者角色都可以有任意数量的观察者。

观察者提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。

public interface Watched  
{  
     public void addWatcher(Watcher watcher);  //添加观察者
  
     public void removeWatcher(Watcher watcher);  //移除观察者
  
     public void notifyWatchers();  //当Watcher方法改变时,这个方法被调用,通知所有的观察者                    
}  

抽象观察者角色:为所有具体的观察者者定义一个接口,在得到主题的通知时更新自己。 

public interface Watcher                                                                                  
{  
     public void update();  
}

具体观察者角色:该角色实现抽象观察者所要求的更新接口,以便使本身的状态与主题的状态相协调。通常用一个子类

实现。如需要,具体观察这角色可以保存一个指向具体主题角色的引用。

public class Security implements Watcher  
{  
     @Override  
     public void update()  
     {  
          System.out.println(“运输车有行动,保安贴身保护");  
     }  
}

具体被观察者角色:在被观察者内部状态改变时,给所有登记过的观察者发出通知,具体被观察者角色通常用一个

子类实现。

public class Transporter implements Watched  
{  
     private List<Watcher> list = new ArrayList<Watcher>();  
  
     @Override  
     public void addWatcher(Watcher watcher)  
     {  
          list.add(watcher);  
     }  
  
     @Override  
     public void removeWatcher(Watcher watcher)  
     {  
          list.remove(watcher);  
     }  
  
     @Override  
     public void notifyWatchers(String str)  
     {  
          for (Watcher watcher : list)  
          {  
               watcher.update();  
          }  
     }  
  
}  
  当需要观察者要知晓被观察者内部的数据变化时,只需在被观察者内部注册观察者的对象即可addWatcher(Watcher

watcher)。当被观察者内部数据发生变化时被观察者操作notifyWatchers(String str)就把改变的数据推送给注册后的观察者。

 2.适用场景:

1).当一个抽象模型有两个方面,其中一个方面依赖另一个方面。将者二者封装在独立的对象中以使它们各自独立地改变

和复用。

2).当一个对象的改变需要同时改变其他对象,而不知道具体有多少对象有待改变。

3).当一个对象必须通知其他对象,而它又不能假定其它对象是谁,换言之,不希望这些对象是紧密耦合的。

 3.维护Observer对象:

  有时我们可能无法创建一个观察类,该类继承自observable类的子类。特别是当该类已经是其他(不是Java内置的

object类)的子类时,我们可以提供一个拥有observable对象的类,该类可以将关键的方法调用转发给observable对象。

Java.awt包中component类就使用了这种方法,但是它用了一个PropertyChangeSupport对象代替了Observable对象。

 

使用observer也好,使用PropertyChangeSupport也好或者其他类也罢,构建观察者模式的要点是要在对象之间建立起一个一

多的关系。当一个对象状态发生改变时,所有依赖它的对象都会被通知,并做出相应的更新。这有助于缩小职责范围,使得维护

观察者和被观察者对象变得轻而易举。

四、调停者(Mediator)模式:

 面向对象开发要求尽可能恰当地分配职责,要求对象能够独立地完成自己的任务。如观察者模式,就是通过最小化对象与

对象之间的职责交互,从而支持职责的合理分配。单例模式是将职责集中在某个对象中以便其他对象的访问与重用。与单例模式

相似,调停者模式也是集中职责,但它是针对一组特殊的对象,而不是系统中全部对象。

 当对象间的交互趋向复杂,而每个对象都需要知道其他对象的情况时,提供一个集中的控制权时很有用的。当相关对象的

交互逻辑独立于对象的其他行为时,职责的集中同样有用。

 调停者模式的意图是定义一个对象,封装一组对象的交互,从而降低对象间的耦合度,避免了对象间的显示引用,并且可以

独立地改变对象的行为。

 1.GUI调停者:

 GUI组件对调停者模式的运用可谓水到渠成,当事件发生时,它会通知调停者而不是直接更新其他组件。GUI应用可能

是最常见的使用调停者模式的程序了。但在其他场景下,可能需要引入调停者模式。

  无论何时,只要对象之间存在复杂的交互行为,就可以将这些交互职责集中到这些对象之外的一个调停者对象中。这就

形成了松耦合--减少了对象间的直接交互。在一个独立类中,管理对象间的交互还能简化与标准化对象间的交互规则。如当需要

管理关系的一致性时,可以使用调停者模式。

 2.调停者的实例运用:

 想象这样一个场景:一个系统内部通过许多的类互相调用来完成一系列的功能,这个系统内部的每个类都会存在至少一次

的调用与被调用,这种情况下,一旦某个类发生问题,进行修改,无疑会影响到所有调用它的类,甚至它调用的类,可见这种情况

类与类之间的耦合性极高(体现为太多的复杂的直接引用)。

  这正是调停者模式的主场,调停者犹如第三方中介一般,将所有的类与类之间的引用都导向调停者类,所有类的请求,

一致发向调停者,由调停者再发向目标类,这样原本复杂的网状的类关系,变成了简单的星型类关系,调停者类位于核心,所有

其他类位于外围,指向调停者。如此这般,类与类之间的调用耦合被解除(通过统一的第三方来发起调用),某个类发生问题,

发生修改,也只会影响到调停者,而不会直接影响到简介发起调用的那些类。

  生活中的例子:一个公司部门,有一个经理来充当调停者,余下的员工充当互相作用的类。

 抽象调停者(Mediator)角色:定义出员工对象到经理对象的接口,其中主要方法时一个(多个)事件方法。

/**
 * 调停者接口                                                                                    
 */
public interface Mediator {
    void change(String message,Zhiyuan zhiyuan,String name);
}

具体调停者(ConcreateMediator)角色:实现了抽象调停者所有声明的事件方法,具体调停者知晓所有员工类,并负责

具体的协调各员工对象的交互关系。

 

/**
 * 调停者--经理
 */
public class JingLi implements Mediator {
    @Override
    public void change(String message, Zhiyuan zhiyuan, String name) {

    }
}

 抽象员工类(Colleague)角色:定义出调停者到员对象那个的接口,员工对象只知道调停者而不知道其余员工对象。

/**
 * 职员的接口
 */
public abstract class Zhiyuan {
    String name;                                                                                     
    private Mediator mMediator;
    public Zhiyuan(Mediator mediator,String name) {
        this.mMediator = mediator;
        this.name = name;
    }

    /**
     * 被调停者调用的方法
     * @param message
     * @param name
     */
    public void called(String message,String name) {

    }

    public void call(String message,Zhiyuan zhiyuan,String name) {
        mMediator.change(message,zhiyuan,name);
    }

}

具体员工类(ConcreateColleague)角色:所有的具体的员工类均从抽象同事类继承而来。实现自己的业务,在需要要

与其他员工通信时,就与持有调停者通信,调停者会负责与其他的同事交互。

 

/**
 * 职员A
 */
public class Zhiyuan1 extends Zhiyuan {
    public Zhiyuan1(Mediator mediator, String name) {
        super(mediator, name);
    }
}


五、代理(Proxy)模式:

 普通对象可以通过公共接口完成自己所需完成工作。然而,有些对象却由于某些原因无法履行自己日常的职责。如有的对象

加载时间过长,有的对象运行在其他的计算机上,或者需要拦截发送到对象的消息。对于这些场景,我们可以引入代理对象,通过

客户端需要的职责,并将相应的请求转发给底层的目标。

 代理模式的意图是通过提供一个代理(Proxy)或者占位符来控制对该对象的访问。

 1.静态代理:

 RealSubject:委托类,Proxy是代理类;

Subject:委托类和代理的接口;

request:委托类和代理类的共同方法。

interface Subject {                                                                                       
          void request();
      }

class RealSubject implements Subject {
    public void request(){
        System.out.println("RealSubject");
    }
}

class Proxy implements Subject {
    private Subject subject;

    public Proxy(Subject subject){
        this.subject = subject;
    }
    public void request(){
        System.out.println("begin");
        subject.request();
        System.out.println("end");
    }
}

public class ProxyTest {
    public static void main(String args[]) {
        RealSubject subject = new RealSubject();
        Proxy p = new Proxy(subject);
        p.request();
    }
}

 2.动态代理:

 动态代理中,代理类并不是在Java代码中实现,而是在运行时期生成,相比静态代理,动态代理可以很方便的对委托

类的方法进行统一处理。

  定义业务逻辑:

public interface Service {                                                                                     
    //目标方法 
    public abstract void add();  
} 

public class UserServiceImpl implements Service {  
    public void add() {  
        System.out.println("This is add service");  
    }  
}
   利用java.lang.reflect.Proxy类和java.lang.reflect.InvoactionHandler接口定义代理类的实现:

class MyInvocatioHandler implements InvocationHandler {                                                        
    private Object target;

    public MyInvocatioHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("-----before-----");
        Object result = method.invoke(target, args);
        System.out.println("-----end-----");
        return result;
    }
    // 生成代理对象
    public Object getProxy() {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        Class<?>[] interfaces = target.getClass().getInterfaces();
        return Proxy.newProxyInstance(loader, interfaces, this);
    }
}
   使用动态代理类:

public class ProxyTest {                                                                                       
    public static void main(String[] args) {
        Service service = new UserServiceImpl();
        MyInvocatioHandler handler = new MyInvocatioHandler(service);
        Service serviceProxy = (Service)handler.getProxy();
        serviceProxy.add();
    }
}


六、职责链(Chain of Responsibility)模式:

面向对象的开发中往往力求对象之间保持松散耦合,确保对象各自的责任具体并能最小化。这样的设计可以使得系统更

加容易修改,同时降低产生缺陷的风险。从某种角度上讲,Java语言有利于做出解耦的设计。客户端通常只能访问对象可见的接

口,而不了解其实现细节。同时,客户端只需要知道哪个对象具有它所需要调用的方法即可。当将若干对象按照某种层次结构进

行组织时,客户端可能事先并不了解应该使用哪个类。这些对象要么执行该方法,要么请求传递给下一个对象。

  责任链模式的目的是通过给予多个对象处理请求的机会,以解除请求的发送者与接收者之间的耦合。意图在于减轻调用

压力,使它们无须了解哪个对象可以处理调用请求。

1.定义:

 使多个对下岗都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系,将这个对象连城一条链,并沿着

条链传递该请求,直到有一个对象处理他为止。

 2.角色定义及实例演示:

 抽象处理者角色(Handler):定义出一个处理请求的接口。如需要,皆苦可以定义出一个方法以设定和返回对下家的引

用。这个角色通常由一个Java抽象类或Java接口实现。 

public abstract class Handler {                                                                               
      
    /** 
     * 持有后继的责任对象 
     */  
    protected Handler successor;  
    /** 
     * 示意处理请求的方法,虽然这个示意方法是没有传入参数的 
     * 但实际是可以传入参数的,根据具体需要来选择是否传递参数 
     */  
    public abstract void handleRequest();  
    /** 
     * 取值方法 
     */  
    public Handler getSuccessor() {  
        return successor;  
    }  
    /** 
     * 赋值方法,设置后继的责任对象 
     */  
    public void setSuccessor(Handler successor) {  
        this.successor = successor;  
    }  
      
}  

 具体处理者角色(ConcreateHandler):具体处理者接收到请求后,可以选择将请求处理掉,或者将请求传给下家。由

于具体处理者持有对下家的引用,因此,如需要具体处理者可以访问下家。

 

public class ConcreteHandler extends Handler {  
    /** 
     * 处理方法,调用此方法处理请求 
     */  
    @Override  
    public void handleRequest() {  
        /** 
         * 判断是否有后继的责任对象 
         * 如果有,就转发请求给后继的责任对象 
         * 如果没有,则处理请求 
         */  
        if(getSuccessor() != null)  
        {              
            System.out.println("放过请求");  
            getSuccessor().handleRequest();              
        }else  
        {              
            System.out.println("处理请求");  
        }  
    }  
  
} 

  客户端的使用: 

public class Client {                                                                                      
  
    public static void main(String[] args) {  
        //组装责任链  
        Handler handler1 = new ConcreteHandler();  
        Handler handler2 = new ConcreteHandler();  
        handler1.setSuccessor(handler2);  
        //提交请求  
        handler1.handleRequest();  
    }  
  
}

 3.小结:

  在运用职责链时,客户端不必事先知道对象集合中哪个对象可提供自己需要的服务。当客户端发出调用请求后,该

请求会沿着职责链转发请求,直到找到提供该服务的对象为止。这就可以降低客户端与提供服务的对象之间的耦合度。

   如果某个对象链能够应用一系列不同的策略某个问题,如解析用户的输入,这时也可应用职责链模式。该模式更

常见于组合结构,它具有一个包容的层次结构,为对象链提供了一种自然的查询顺序。简化对象链和客户端的代码是职责链的一

个主要优点。


七、享元(Flyweight)模式:

  享元模式在客户对象间提供共享对象,并且为共享对象创建职责,以便普通对象不需要考虑共享对象创建的问题。通常情况下

任何时候都只能有一个客户对象引用该共享对象。当某个客户对象改变该共享对象的状态时,该共享对象不需要通知其他客户对象

然而有时候可能需要让多个客户对象同时共享访问某个对象。

  享元模式的意图是通过共享有效地支持大量细粒度的对象。

  1.不变性:

  享元模式让对个客户对象间共享访问限定数量的对象。为了实现这个目的,你必须要考虑到当某个对象改变了该共享对像

的状态时,该状态变化会影响到每个访问它的对象。当多个客户对象要共享访问某个对象时,若要保证对象间不会互相影响,一

种最简单而又常用的做法是,限制客户对象调用任何可能改变共享对象的方法。可以通过将对象设置为不变(immutable)类型

来达到这一目的。然而,这样做会导致对象在创建后无法改变。Java中最常见的不可变对象String类。当创建一个String对象后

无论你还是其他客户对象都无法改变该对象的字符。

 若存在大量相似对象,而可能需要共享访问它们,但它们可能并非一成不变。在这种情况下,使用享元模式的第一步是

对象中不可变得部分抽取出来,共享它们。

 2.享元模式的解析及实例示例:

 在Flyweight模式中,由于要产生各种各样的对象,所以在Flyweight(享元)模式中常出现Factory模式。Flyweight的

内部状态是用来共享的,Flyweight factory负责维护一个对象池(Flyweight Pool)l来存放内部状态的对象。Flyweight模式是

一个提高程序效率和性能的模式,会大大加快程序的运行速度。

  定义一个抽象Flyweight类: 

package Flyweight;  
public abstract class Flyweight{  
 public abstract void operation();  
}

  实现具体类:

public class ConcreteFlyweight extends Flyweight{                                                             
 private String string;  
 public ConcreteFlyweight(String str){  
  string = str;  
 }  
 public void operation()  
 {  
  System.out.println("Concrete---Flyweight : " + string);  
 }  
} 

实现工厂方法类:在1处定义Hashtable用来储存各个对象;在2处选出要实例化的对象,在6处将对象返回,如在

Hashtable中没有要选择的对象,此时变量flywiget为null,产生一个新的flyweight储存在Hashtable中,并将对象返回。

public class FlyweightFactory{  
 private Hashtable flyweights = new Hashtable();//----------------------------1  
 public FlyweightFactory(){}  
 public Flyweight getFlyWeight(Object obj){  
  Flyweight flyweight = (Flyweight) flyweights.get(obj);//----------------2  
  if(flyweight == null){//---------------------------------------------------3  
   //产生新的ConcreteFlyweight  
   flyweight = new ConcreteFlyweight((String)obj);  
   flyweights.put(obj, flyweight);//--------------------------------------5  
  }  
  return flyweight;//---------------------------------------------------------6  
 }  
 public int getFlyweightSize(){  
  return flyweights.size();  
 }  
}  

 3.小结:

 享元模式可以使共享地访问那些大量出现的细粒度对象,享元对象必须是不可变得,可以将那些需要共享访问,并且

不变得部分提取出来。为了确保享元对象能够被共享,需要提供并强制客户对象使用享元工厂来查找享元对象。访问修饰符对其他

开发者进行了一定的限制,但是内部类的使用使限制更进一步,完全限制了类仅能由其他外部容器访问,在确保客户对象正确地

使用享元工厂后,就可以提供大量细粒度对象的安全共享访问了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值