状态模式
1、定义与特点
- 定义:对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
- 优点:
-
- 状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”。
- 减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖。
- 有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。
- 缺点:
-
- 状态模式的使用必然会增加系统的类与对象的个数。
- 状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱。
2、结构与实现
状态模式把受环境改变的对象行为包装在不同的状态对象里,其意图是让一个对象在其内部状态改变的时候,其行为也随之改变。
- 模式的结构:
-
- 环境(Context)角色:也称为上下文,它定义了客户感兴趣的接口,维护一个当前状态,并将与状态相关的操作委托给当前状态对象来处理。
- 抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为。
- 具体状态(Concrete State)角色:实现抽象状态所对应的行为。
- 模式的实现:
public class StatePatternClient{
public static void main(String[] args){
Context context=new Context(); //创建环境
context.Handle(); //处理请求
context.Handle();
context.Handle();
context.Handle();
}
}
//环境类
class Context{
private State state;
//定义环境类的初始状态
public Context(){
this.state=new ConcreteStateA();
}
//设置新状态
public void setState(State state){
this.state=state;
}
//读取状态
public State getState(){
return(state);
}
//对请求做处理
public void Handle(){
state.Handle(this);
}
}
//抽象状态类
abstract class State{
public abstract void Handle(Context context);
}
//具体状态A类
class ConcreteStateA extends State{
public void Handle(Context context){
System.out.println("当前状态是 A.");
context.setState(new ConcreteStateB());
}
}
//具体状态B类
class ConcreteStateB extends State{
public void Handle(Context context){
System.out.println("当前状态是 B.");
context.setState(new ConcreteStateA());
}
}
3、应用场景
- 当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式
- 一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态时。
4、扩展
- 在有些情况下,可能有多个环境对象需要共享一组状态,这时需要引入享元模式,将这些具体状态对象放在集合中供程序共享,其结构图如图 5 所示。
import java.util.HashMap;
public class FlyweightStatePattern{
public static void main(String[] args){
ShareContext context=new ShareContext(); //创建环境
context.Handle(); //处理请求
context.Handle();
context.Handle();
context.Handle();
}
}
//环境类
class ShareContext{
private ShareState state;
private HashMap<String, ShareState> stateSet=new HashMap<String, ShareState>();
public ShareContext(){
state=new ConcreteState1();
stateSet.put("1", state);
state=new ConcreteState2();
stateSet.put("2", state);
state=getState("1");
}
//设置新状态
public void setState(ShareState state){
this.state=state;
}
//读取状态
public ShareState getState(String key){
ShareState s=(ShareState)stateSet.get(key);
return s;
}
//对请求做处理
public void Handle(){
state.Handle(this);
}
}
//抽象状态类
abstract class ShareState{
public abstract void Handle(ShareContext context);
}
//具体状态1类
class ConcreteState1 extends ShareState{
public void Handle(ShareContext context){
System.out.println("当前状态是: 状态1");
context.setState(context.getState("2"));
}
}
//具体状态2类
class ConcreteState2 extends ShareState{
public void Handle(ShareContext context){
System.out.println("当前状态是: 状态2");
context.setState(context.getState("1"));
}
}
观察者模式
1、定义与特点
- 定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。
- 优点:
-
- 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
- 目标与观察者之间建立了一套触发机制。
- 缺点:
-
- 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
- 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
2、结构与实现
实现观察者模式时要注意具体目标对象和具体观察者对象之间不能直接调用,否则将使两者之间紧密耦合起来,这违反了面向对象的设计原则。
- 模式的结构:
-
- 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
- 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
- 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
- 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
- 模式的实现:
import java.util.*;
public class ObserverPattern{
public static void main(String[] args){
Subject subject=new ConcreteSubject();
Observer obs1=new ConcreteObserver1();
Observer obs2=new ConcreteObserver2();
subject.add(obs1);
subject.add(obs2);
subject.notifyObserver();
}
}
//抽象目标
abstract class Subject{
protected List<Observer> observers=new ArrayList<Observer>();
//增加观察者方法
public void add(Observer observer){
observers.add(observer);
}
//删除观察者方法
public void remove(Observer observer){
observers.remove(observer);
}
public abstract void notifyObserver(); //通知观察者方法
}
//具体目标
class ConcreteSubject extends Subject{
public void notifyObserver(){
System.out.println("具体目标发生改变...");
System.out.println("--------------");
for(Object obs:observers){
((Observer)obs).response();
}
}
}
//抽象观察者
interface Observer{
void response(); //反应
}
//具体观察者1
class ConcreteObserver1 implements Observer{
public void response(){
System.out.println("具体观察者1作出反应!");
}
}
//具体观察者1
class ConcreteObserver2 implements Observer{
public void response(){
System.out.println("具体观察者2作出反应!");
}
}
3、应用场景
- 对象间存在一对多关系,一个对象的状态发生改变会影响其他对象。
- 当一个抽象模型有两个方面,其中一个方面依赖于另一方面时,可将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
4、扩展
- 在 Java 中,通过 java.util.Observable 类和 java.util.Observer 接口定义了观察者模式,只要实现它们的子类就可以编写观察者模式实例。
-
- Observable类:Observable 类是抽象目标类,它有一个 Vector 向量,用于保存所有要通知的观察者对象,下面来介绍它最重要的 3 个方法:
-
-
- void addObserver(Observer o) 方法:用于将新的观察者对象添加到向量中。
- void notifyObservers(Object arg) 方法:调用向量中的所有观察者对象的 update。方法,通知它们数据发生改变。通常越晚加入向量的观察者越先得到通知。
- void setChange() 方法:用来设置一个 boolean 类型的内部标志位,注明目标对象发生了变化。当它为真时,notifyObservers() 才会通知观察者。
-
-
- Observer 接口:Observer 接口是抽象观察者,它监视目标对象的变化,当目标对象发生变化时,观察者得到通知,并调用 void update(Observable o,Object arg) 方法,进行相应的工作。
//利用 Observable 类和 Observer 接口实现原油期货的观察者模式实例;图 5 所示是其结构图。
import java.util.Observer;
import java.util.Observable;
public class CrudeOilFutures{
public static void main(String[] args){
OilFutures oil=new OilFutures();
Observer bull=new Bull(); //多方
Observer bear=new Bear(); //空方
oil.addObserver(bull);
oil.addObserver(bear);
oil.setPrice(10);
oil.setPrice(-8);
}
}
//具体目标类:原油期货
class OilFutures extends Observable{
private float price;
public float getPrice(){
return this.price;
}
public void setPrice(float price){
super.setChanged() ; //设置内部标志位,注明数据发生变化
super.notifyObservers(price); //通知观察者价格改变了
this.price=price ;
}
}
//具体观察者类:多方
class Bull implements Observer{
public void update(Observable o,Object arg){
Float price=((Float)arg).floatValue();
if(price>0){
System.out.println("油价上涨"+price+"元,多方高兴了!");
}else{
System.out.println("油价下跌"+(-price)+"元,多方伤心了!");
}
}
}
//具体观察者类:空方
class Bear implements Observer{
public void update(Observable o,Object arg){
Float price=((Float)arg).floatValue();
if(price>0){
System.out.println("油价上涨"+price+"元,空方伤心了!");
}else{
System.out.println("油价下跌"+(-price)+"元,空方高兴了!");
}
}
}
中介者模式
1、定义与特点
- 定义:定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。
- 优点:
-
- 降低了对象之间的耦合性,使得对象易于独立地被复用。
- 将对象间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。
- 缺点:当同事类太多时,中介者的职责将很大,它会变得复杂而庞大,以至于系统难以维护
2、结构与实现
- 模式的结构:
-
- 抽象中介者(Mediator)角色:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法。
- 具体中介者(ConcreteMediator)角色:实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色。
- 抽象同事类(Colleague)角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能。
- 具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。
- 模式的实现:
import java.util.*;
public class MediatorPattern{
public static void main(String[] args){
Mediator md=new ConcreteMediator();
Colleague c1,c2;
c1=new ConcreteColleague1();
c2=new ConcreteColleague2();
md.register(c1);
md.register(c2);
c1.send();
System.out.println("-------------");
c2.send();
}
}
//抽象中介者
abstract class Mediator{
public abstract void register(Colleague colleague);
public abstract void relay(Colleague cl); //转发
}
//具体中介者
class ConcreteMediator extends Mediator{
private List<Colleague> colleagues=new ArrayList<Colleague>();
public void register(Colleague colleague){
if(!colleagues.contains(colleague)){
colleagues.add(colleague);
colleague.setMedium(this);
}
}
public void relay(Colleague cl){
for(Colleague ob:colleagues){
if(!ob.equals(cl)){
((Colleague)ob).receive();
}
}
}
}
//抽象同事类
abstract class Colleague{
protected Mediator mediator;
public void setMedium(Mediator mediator){
this.mediator=mediator;
}
public abstract void receive();
public abstract void send();
}
//具体同事类
class ConcreteColleague1 extends Colleague{
public void receive(){
System.out.println("具体同事类1收到请求。");
}
public void send(){
System.out.println("具体同事类1发出请求。");
mediator.relay(this); //请中介者转发
}
}
//具体同事类
class ConcreteColleague2 extends Colleague{
public void receive(){
System.out.println("具体同事类2收到请求。");
}
public void send(){
System.out.println("具体同事类2发出请求。");
mediator.relay(this); //请中介者转发
}
}
3、应用场景
- 当对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用时。
- 当想创建一个运行于多个类之间的对象,又不想生成新的子类时。
4、扩展
- 在实际开发中,通常采用以下两种方法来简化中介者模式,使开发变得更简单。
-
- 不定义中介者接口,把具体中介者对象实现成为单例。
- 同事对象不持有中介者,而是在需要的时f矣直接获取中介者对象并调用。
迭代器模式
1、定义与特点
- 定义:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。迭代器模式是一种对象行为型模式。
- 优点:
-
- 访问一个聚合对象的内容而无须暴露它的内部表示。
- 遍历任务交由迭代器完成,这简化了聚合类。
- 它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
- 增加新的聚合类和迭代器类都很方便,无须修改原有代码。
- 封装性良好,为遍历不同的聚合结构提供一个统一的接口
- 缺点:增加了类的个数,这在一定程度上增加了系统的复杂性。
2、结构与实现
迭代器模式是通过将聚合对象的遍历行为分离出来,抽象成迭代器类来实现的,其目的是在不暴露聚合对象的内部结构的情况下,让外部代码透明地访问聚合的内部数据。
- 模式的结构:
-
- 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
- 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
- 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。
- 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
- 模式的实现:
import java.util.*;
public class IteratorPattern{
public static void main(String[] args){
Aggregate ag=new ConcreteAggregate();
ag.add("中山大学");
ag.add("华南理工");
ag.add("韶关学院");
System.out.print("聚合的内容有:");
Iterator it=ag.getIterator();
while(it.hasNext()){
Object ob=it.next();
System.out.print(ob.toString()+"\t");
}
Object ob=it.first();
System.out.println("\nFirst:"+ob.toString());
}
}
//抽象聚合
interface Aggregate{
public void add(Object obj);
public void remove(Object obj);
public Iterator getIterator();
}
//具体聚合
class ConcreteAggregate implements Aggregate{
private List<Object> list=new ArrayList<Object>();
public void add(Object obj){
list.add(obj);
}
public void remove(Object obj){
list.remove(obj);
}
public Iterator getIterator(){
return(new ConcreteIterator(list));
}
}
//抽象迭代器
interface Iterator{
Object first();
Object next();
boolean hasNext();
}
//具体迭代器
class ConcreteIterator implements Iterator{
private List<Object> list=null;
private int index=-1;
public ConcreteIterator(List<Object> list){
this.list=list;
}
public boolean hasNext(){
if(index<list.size()-1){
return true;
}else{
return false;
}
}
public Object first(){
index=0;
Object obj=list.get(index);;
return obj;
}
public Object next(){
Object obj=null;
if(this.hasNext()){
obj=list.get(++index);
}
return obj;
}
}
3、应用场景
- 当需要为聚合对象提供多种遍历方式时。
- 当需要为遍历不同的聚合结构提供一个统一的接口时。
- 当访问一个聚合对象的内容而无须暴露其内部细节的表示时。
4、扩展
- 迭代器模式常常与组合模式结合起来使用,在对组合模式中的容器构件进行访问时,经常将迭代器潜藏在组合模式的容器构成类中。当然,也可以构造一个外部迭代器来对容器构件进行访问,其结构图如图 4 所示。
更多设计模式的分享,请查看https://blog.csdn.net/pp_l_ly/category_9961646.html