行为型模式
主要是对对象的行为进行设计,解决对象间的联系问题也可以理解为解决对象与对象间的通信。
续前3篇
设计模式初篇
设计模式精髓篇之创建型
设计模式精髓篇之结构型
模板方法模式
目标:
定义一个算法操作框架,将一些步骤延迟到子类中,使得子类可以不改变整个算法的操作的顺序操作结构但可以重新定义算法中特定的步骤的具体实现。
why-思考:
当一个完整的算法操作流程的结构是固定的,但是其中一些特定的步骤的具体实现是多变的,如何在固定的步骤中,分化出多变的实现,提升算法的可扩展性?
how-思考:
将多变的实现部分给抽取出来,先整体把握整个算法的思路流程。
JDK中的示例:
InputStream 类的read方法
public abstract class InputStream implements Closeable {
//1.定义出你的大体的算法步骤,其中read()函数是步骤之一,但没具体实现,因为该函数内的实现是多变的
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read();//待具体实现,这已经属于是一个固定的算法操作流程中的步骤
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
//2.抽取出要具体实现的部分
public abstract int read() throws IOException;
}
//3.交给子类去具体实现
public class FileInputStream extends InputStream
{
public int read() throws IOException {
return read0();
}
private native int read0() throws IOException;//调用的是Native方法,c库中的方法。使用的是JNI机制
}
迭代器模式
目标:
提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
why-思考:
怎么做到能在不暴露对象内部的情况下又能顺利的访问到聚合对象中的各个元素呢?
how-思考:
要想做到不暴露对象的内部情况,首先就是不能直接通过调要组合对象的方法来访问组合对象中的各个元素。此时,应想到代理的方式,将访问组合对象的各个元素的方法抽象出去成为一个代理对象。
JDK中的示例:
java.util.Iterator
//1.抽象出来的访问聚合对象的各个元素的方法的接口
public interface Iterator<E> {
public boolean hasNext();
public boolean hasNext();
public void remove();
}
//2.具体实现该操作的接口函数
private class ArrayListIterator implements Iterator<E> {
ArrayList<E> ourList = ArrayList.this;//需关联到要遍历的组合对象
......//具体实现的接口方法
}
//3.具体的聚合对象ArrayList 需对外提供生成 操作各个元素的接口。
//ArrayList类中包括该方法
public Iterator<E> iterator() {
return new ArrayListIterator();
}
观察者模式
目标:
当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。是一个观察者和被观察者的一个对象依赖关系
why-思考:
因为所有的观察者可能各不一样,怎么将观察者统一起来,并依赖到观察者中,这是一个难点。当被观察者的状态发生改变时,怎么做到即时通知依赖它的观察者并更新?
how-思考:
首先得将观察者统一起来,都统一实现同一接口即可统一,然后,提取出观察者观察到状态改变时需要更新的公共部分。
JDK中的示例:
EventListener
//1.JDK为观察者提供的统一接口
public interface EventListener {
/* empty */
}
//2.扩展统一接口
public interface MyEventListener extends EventListener {
public void update();
}
//3.观察者实现统一接口,可以定义类A,类B等不同的观察者
public class A implements MyEventListener {
Subject subject;//需要关联的被观察者对象
//3.1将观察者依赖到被观察者上
public A(Subject subject){
this.subject = subject;
this.subject.attach(this);
}
//3.2被观察者状态改变后的更新操作
@Override public void update() {
System.out.println( "Binary String: "
+ Integer.toBinaryString( subject.getState() ) );
}
}
//4.定义被观察者
public class Subject {
private List<MyEventListener> observers = new ArrayList<MyEventListener >();
//4.1依赖到观察者
public void attach(Observer observer){
observers.add(observer);
}
//4.2状态改变,通知观察者
public void setState(int state) {
this.state = state;
notifyAllObservers();
}
public void notifyAllObservers(){
for (Observer observer : observers) {
observer.update();
}
}
}
责任链模式
目标:
描述的是一个请求与多个请求处理者的一个关系。多个处理者由每一个处理者对其下家的引用而连接起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。此时,哪个处理者处理该请求由运行时刻自动确定,或者可以动态指定哪个处理者处理请求。
why-思考:
如何将多个处理者通过引用连接起来?如何添加处理者?如何动态的指定处理者去处理请求。
how-思考:
处理者间需要以链表的形式,包含指向下个处理者的引用。添加处理者可以指向下个处理者,也可以不指向,不指向的时候表示处理者链到结尾了。通过抽象出处理者中共同的处理函数来使处理者由不同的处理方式。
JDK中的示例:
java web中的javax.servlet.Filter
//方式一,实现介绍常规例子
//1.设置抽象处理者,并将处理者与下个处理者串联起来
abstract class AbHandler {
private AbHandler nextHandler;
public void setNextHandler(AbHandler nextHandler){
this.nextHandler=nextHandler;
}
public AbHandler getNetHandler(){
return nextHandler;
}
abstract public void doHandler();
}
//2.具体处理者的实现
public class ConcernHandler{
void doHandler(){
//2.1决定具体实现不实现
......
//2.2处理事件继续下去
if(getNetHandler!=null){
getNetHandler().doHandler();
}
}
}
//方式二 javax.servlet.Filter处理者--------------
//一般定义Filter需要在web.xml配置中进行,可以定义多个
public class AFilter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
.........//处理者处理
//chain为处理链,这里它不是使用的Filter处理者依赖下一个处理者Filter的方式,而是使用源码中具体实现FilterChain的接口的类中数组管理Filter的方式来依赖下一个
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
策略模式
目标:
一个类的行为或其算法可以在运行时更改。
why-思考:
如何做到类中的行为可更改。类中的行为可理解为类中的函数。
how-思考:
面向接口编程,实现接口或者抽象函数可实现多变性。
JDK中的示例:
javax.servlet.http.HttpServlet
//分两种情况
//情况一,(不是JDK中的例子)。通过实现SerMethod 接口,可以有多个不同的行为。
public class A{
//定义可多变性的多态接口成员对象。省略了基本的set,get方法
SerMethod serMethod;
public void method1(){
serMethod.dothing()
}
}
public interface SerMethod{
public void dothing();
}
//情况二
//其中HttpServletRequest和HttpServletResponse是接口类型,因此该doGet方法可以因为实现上面两个接口构造出不同的对象,来改变doGet内部的行为。
public abstract class HttpServlet extends GenericServlet{
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if(protocol.endsWith("1.1")) {
resp.sendError(405, msg);
} else {
resp.sendError(400, msg);
}
}
}
//总之,面向接口编程,多变的进行封装成接口
命令模式
目标:
为了解决命令的请求者和命令的实现者之间的耦合关系。一方面,方便的对命令进行扩展;另一方面,对多个命令的统一控制。
why-思考:
如何实现请求者和命令的实现者的解耦?如何实现对命令的控制?
how-思考:
首先出现解耦,就会想到使用接口,因为接口可以使类与类之间的强耦合关系降低。定义一个命令接口,这样可以实现对多个命令的扩展,也可以实现解耦。
JDK中的示例:
java.lang.Runnable(命令)
//1.定义命令接口
public interface Runnable {
public abstract void run();
}
//2.定义接收者-如果接收者有多个且包含共同特征则定义接口,否则直接定义一个类即可
public interface IReceiver {
public abstract void doSomeThing();
}
//3.定义具体的命令,并关联命令的接受者。
public class ConcreteThread implements Runnable{
private IReceiver ireceiver;//此出省略set和get方法
@Override
public void run(){
.....
ireceiver.doSomeThing();
......
}
}
//4.定义命令的请求者
public class Invoker
{
//4.1需要关联发送的命令,通过关联了命令,实现与接受者IReceiver 解耦
private ICommand commandA = null;
public void SetCommandA(ICommand commandA)
{
this.commandA = commandA;
}
//4.2执行命令A---对命令的控制
public void RunCommandA()
{
commandA.Execute();
}
//5具体实现
Invoker invoker=new Invoker ();
invoker.SetCommandA(new ConcreteThread (new ConcreteReceiver()))
invoker.RunCommandA();
状态模式
目标:
对象中的内置状态改变时相应的允许改变其行为。
why-思考:
怎么将对象的内部对象和其行为联系起来?
how-思考:
联系起来并不是简单的将状态作为对象的内部方法的形参这么简单,这样会使得对象的那个内部方法显得很臃肿,应该将状态和状态改变的行为封装到一块。
普通示例:
//1.首先定义状态和状态行为。
public interface State{
//状态行为
public void dothing();
}
//2.有状态的对象的类
public void Context{
private State state;//省略了基本的set,get方法
public void test(){
state.dothing();//根据状态做状态内对应的状态行为
}
}
备忘录模式
目标:
在不破坏封装的条件下,将一个对象的状态捕捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。
why-思考:
怎么保证不破坏封装的情况下,不被其他对象读取?这是重点
how-思考:
首先将需要备忘的内容交给唯一管理对象类备忘录角色。再将备忘录角色交给总负责人创建,总负责人不对备忘录对象里的备忘内容查看。
示例:
//1.定义一个发起人角色(需要备忘的对象),负责创建一个含有当前的内部状态的备忘录对象,使用备忘录对象存储自身内部的状态。
public class Originator {
private String state;//需要备忘的值,省略了基本的set,get方法
//1.1创建备忘录对象
public Memento createMemento(){
return new Memento(state);
}
//根据备忘录对象的状态恢复状态
public void restoreMemento(Memento memento){
this.state = memento.getState();
}
}
//2.定义一个备忘录角色类,负责管理需要备忘的值,相当于Originator 的备忘的内容得代理
public class Memento {
private String state;
public Memento(String state){
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
//3.定义一个总负责人的角色,负责保存备忘录对象,这可以需要做到不查看备忘内容。
public class Caretaker {
private Memento memento;
// 备忘录的取值方法
public Memento retrieveMemento(){
return this.memento;
}
//备忘录的赋值方法
public void saveMemento(Memento memento){
this.memento = memento;
}
}
访问者模式
目标:
封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变。
why-思考:
如何做到将数据结构和数据操作进行分离,或者说解耦?
how-思考:
在被访问的类(代表数据结构)里面加一个对外提供接待访问者(代表数据操作)的接口。
示例:
//1.定义数据结构(被访问的类)里面需要包含接收统一的数据操作(访问者)的方法。
public abstract class Employee {
// 接收/引用一个抽象访问者对象 ,department为抽象的数据操作(访问者)
public abstract void accept(Department department);
}
//1.1具体的数据结构即被访问者,可以有多个被访问者
public class ConcreteEmployee extends Employee {
......//数据结构的基本成员变量
@Override
public void accept(Department department) {
department.visit(this);
}
}
//2.定义数据操作(访问的类),包含对每种具体的被访问者(数据结构)声明一个访问操作
public abstract class Department {
......//访问者操作的多个数据结构,因为被访问者可以有多个
public abstract void visit(ConcreteEmployee me);
public abstract void visit(ConcreteEmployee2 me);
.....
}
//2.1定义具体的数据操作,访问者
public class ConcreteDepartment extends Department {
@Override
public void visit(ConcreteEmployee me) {
.....//具体操作ConcreteEmployee 中的数据结构的
}
}
//3.具体实现
Employee el=new ConcreteEmployee ();//3.1定义数据结构(被访问者)
Department dm=new ConcreteDepartment ();//3.2定义具体的数据操作(访问者)
//3.3定义数据操作接收的数据操作,accpet函数可以接收不同的数据操作,但是el数据结构是不变的
el.accept(dm);
感想
1.不同模块和操作角色间的最好解耦方式,就是将容易多变的部分接口化,**面向接口编程**。
2.对象的行为(方法)要么是依赖于其内部成员变量,要么是依赖于外部的其他对象。如果该对象的行为会因为依赖的对象或者因为系统要进行扩展而不唯一时,此时应该将依赖的对象的类进行抽象,利用多态的方式依赖于该对象。
3.**多变的部分一定得进行封装,抽象化**