責任鏈模式

責任鏈模式物件導向程式設計裏是一種軟件設計模式,它包含了一些命令對象和一系列的處理對象。每一個處理對象決定它能處理哪些命令對象,它也知道如何將它不能處理的命令對象傳遞給該鏈中的下一個處理對象。該模式還描述了往該處理鏈的末尾添加新的處理對象的方法。

代碼示例

以下的日誌類(logging)例子演示了該模式。 每一個logging handler首先決定是否需要在該層做處理,然後將控制傳遞到下一個logging handler。程序的輸出是:

  Writting to stdout: Entering function y.
  Writting to stdout: Step1 completed.
  Sending via e-mail:      Step1 completed.
  Writting to stdout: An error has occurred.
  Sending via e-mail:      An error has occurred.
  Writing to stderr:       An error has occurred.

注意:該例子不是日誌類的推薦實現方式。

同時,需要注意的是,通常在責任鏈模式的實現中,如果在某一層已經處理了這個logger,那麼這個logger就不會傳遞下去。在我們這個例子中,消息會一直傳遞到最底層不管它是否已經被處理。

import java.util.*;

abstract class Logger 
{
    public static int ERR = 3;
    public static int NOTICE = 5;
    public static int DEBUG = 7;
    protected int mask;

    // The next element in the chain of responsibility
    protected Logger next;
    public Logger setNext( Logger l)
    {
        next = l;
        return this;
    }

    public final void message( String msg, int priority )
    {
        if ( priority <= mask ) 
        {
            writeMessage( msg );
            if ( next != null )
            {
                next.message( msg, priority );
            }
        }
    }
    
    protected abstract void writeMessage( String msg );

}

class StdoutLogger extends Logger 
{

    public StdoutLogger( int mask ) { this.mask = mask; }

    protected void writeMessage( String msg )
    {
        System.out.println( "Writting to stdout: " + msg );
    }
}


class EmailLogger extends Logger 
{

    public EmailLogger( int mask ) { this.mask = mask; }

    protected void writeMessage( String msg )
    {
        System.out.println( "Sending via email: " + msg );
    }
}

class StderrLogger extends Logger 
{

    public StderrLogger( int mask ) { this.mask = mask; }

    protected void writeMessage( String msg )
    {
        System.out.println( "Sending to stderr: " + msg );
    }
}

public class ChainOfResponsibilityExample
{
    public static void main( String[] args )
    {
        // Build the chain of responsibility
        Logger l = new StdoutLogger( Logger.DEBUG).setNext(
                            new EmailLogger( Logger.NOTICE ).setNext(
                            new StderrLogger( Logger.ERR ) ) );

        // Handled by StdoutLogger
        l.message( "Entering function y.", Logger.DEBUG );

        // Handled by StdoutLogger and EmailLogger
        l.message( "Step1 completed.", Logger.NOTICE );

        // Handled by all three loggers
        l.message( "An error has occurred.", Logger.ERR );
    }
}
<?php
abstract class Logger {
	const ERR = 3;
	const NOTICE = 5;
	const DEBUG = 7;

	protected $mask;
	protected $next; // The next element in the chain of responsibility

	public function setNext(Logger $l) {
		$this->next = $l;
		return $this;
	}

	abstract public function message($msg, $priority);
}

class DebugLogger extends Logger {
	public function __construct($mask) {
		$this->mask = $mask;
	}

	public function message($msg, $priority) {
		if ($priority <= $this->mask) {
			echo "Writting to stdout: {$msg}\n";
		}

		if (false == is_null($this->next)) {
			$this->next->message($msg, $priority);
		}
	}
}

class EmailLogger extends Logger {
	public function __construct($mask) {
		$this->mask = $mask;
	}

	public function message($msg, $priority) {
		if ($priority <= $this->mask) {
			echo "Sending via email: {$msg}\n";
		}

		if (false == is_null($this->next)) {
			$this->next->message($msg, $priority);
		}
	}
}

class StderrLogger extends Logger {
	public function __construct($mask) {
		$this->mask = $mask;
	}

	public function message($msg, $priority) {
		if ($priority <= $this->mask) {
			echo "Writing to stderr: {$msg}\n";
		}

		if (false == is_null($this->next)) {
			$this->next->message($msg, $priority);
		}
	}
}

class ChainOfResponsibilityExample {
	public function __construct() {
		// build the chain of responsibility
		$l = new DebugLogger(Logger::DEBUG);
		$e = new EmailLogger(Logger::NOTICE);
		$s = new StderrLogger(Logger::ERR);
		
		$e->setNext($s);
		$l->setNext($e);

		$l->message("Entering function y.",		Logger::DEBUG);		// handled by DebugLogger
		$l->message("Step1 completed.",			Logger::NOTICE);	// handled by DebugLogger and EmailLogger
		$l->message("An error has occurred.",	Logger::ERR);		// handled by all three Loggers
	}
}

new ChainOfResponsibilityExample();
?>

這是一個用mutex保護對於stream的操作的真實例子。

First the outputStream interface (a simplified version of the real one):

interface outputStream
   predicates
      write : (...).
      writef : (string FormatString, ...).
end interface outputStream

This class encapsulates each of the stream operations the mutex. The finally predicate is used to ensure that the mutex is released no matter how the operation goes (i.e. also in case of exceptions). The underlying mutex object is released by a finalizer in the mutex class, and for this example we leave it at that.

class outputStream_protected : outputStream
   constructors
      new : (string MutexName, outputStream Stream).
end class outputStream_protected

#include @"pfc\multiThread\multiThread.ph"

implement outputStream_protected
   facts
      mutex : mutex.
      stream : outputStream.
   clauses
      new(MutexName, Stream) :-
         mutex := mutex::createNamed(MutexName, false), % do not take ownership
         stream := Stream.
   clauses
      write(...) :-
         _ = mutex:wait(),  % ignore wait code in this simplified example
         finally(stream:write(...), mutex:release()).

   clauses
      writef(FormatString, ...) :-
         _ = mutex:wait(),  % ignore wait code in this simplified example
         finally(stream:writef(FormatString, ...), mutex:release()).
end implement outputStream_protected

Usage example.

The client uses an outputStream. Instead of receiving the pipeStream directly, it gets the protected version of it.

#include @"pfc\pipe\pipe.ph"

goal
   Stream = pipeStream::openClient("TestPipe"),
   Protected = outputStream_protected::new("PipeMutex", Stream),
   client(Protected),
   Stream:close().

另見

外部連結