七、状态模式
最重要的是与策略模式的区别:
策略模式中,是Client提供了不同的策略给Context;状态模式中,状态转移由Context或State自己管理。
状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了他的类。 适用场景: 一个对象的行为取决于他的状态,并且它必须在运行时根据状态改变它的行为; 一个操作中含有庞大的多分支结构,并且这些分支决定于对象的状态。 类图:
模式的组成: 环境类Context: 定义客户感兴趣的接口。维护一个ConcreteState子类的实例,这个实例定义当前状态; 抽象状态类State: 定义一个接口以封装与Context的一个特定状态相关的行为; 具体状态类ConcreteState: 每一子类实现一个与Context的一个状态相关的行为; 实例:push显示红-〉黄-〉绿,pull显示绿-〉黄-〉红
public interface State
{
public void handlePush(Context context);
public void handlePull(Context context);
public String getColor();
}
public class Context
{
private State state = null;
public State getState()
{
return state;
}
public void setState(final State state)
{
this.state = state;
}
public void push()
{
state.handlePush(this);
}
public void pull()
{
state.handlePull(this);
}
}
//RED
public class ConcreteStateA implements State
{
@Override
public void handlePush(final Context context)
{
System.out.print(this.getColor());
final State state = new ConcreteStateB();
context.setState(state);
state.handlePush(context);
}
@Override
public void handlePull(final Context context)
{
System.out.print(this.getColor());
}
@Override
public String getColor()
{
return "RED";
}
}
//YELLOW
public class ConcreteStateB implements State
{
@Override
public void handlePush(final Context context)
{
System.out.print(this.getColor());
final State state = new ConcreteStateC();
context.setState(state);
state.handlePush(context);
}
@Override
public void handlePull(final Context context)
{
System.out.print(this.getColor());
final State state = new ConcreteStateA();
context.setState(state);
state.handlePull(context);
}
@Override
public String getColor()
{
return "YELLOW";
}
}
//green
public class ConcreteStateC implements State
{
@Override
public void handlePush(final Context context)
{
System.out.print(this.getColor());
}
@Override
public void handlePull(final Context context)
{
System.out.print(this.getColor());
final State state = new ConcreteStateB();
context.setState(state);
state.handlePull(context);
}
@Override
public String getColor()
{
return "GREEN";
}
} public class Client
{
public static void main(final String[] args)
{
// YTODO Auto-generated method stub
final Context context = new Context();
context.setState(new ConcreteStateA());
System.out.println("PUSH:");
context.push();
System.out.println("\nPULL:");
context.pull();
}
}
结果: PUSH: REDYELLOWGREEN PULL: GREENYELLOWRED
八、桥模式
类图:
根据业务的功能要求,业务的变化具有两个维度,一个维度是抽象的消息,包括普通消息、加急消息和特急消息,这几个抽象的消息本身就具有一定的关系,加急消息和特急消息会扩展普通消息;另一个维度是在具体的消息发送方式上,包括系统内短消息、邮件和手机短消息,这几个方式是平等的,可被切换的方式。
现在出现问题的根本原因,就在于消息的抽象和实现是混杂在一起的,这就导致了一个纬度的变化会引起另一个纬度进行相应的变化,从而使得程序扩展起来非常困难。
要想解决这个问题,就必须把这两个纬度分开,也就是将抽象部分和实现部分分开,让它们相互独立,这样就可以实现独立的变化,使扩展变得简单。抽象部分就是各个消息的类型所对应的功能,而实现部分就是各种发送消息的方式。按照桥梁模式的结构,给抽象部分和实现部分分别定义接口,然后分别实现它们就可以了。
源代码
抽象消息类
public abstract class AbstractMessage { //持有一个实现部分的对象 MessageImplementor impl; /** * 构造方法,传入实现部分的对象 * @param impl 实现部分的对象 */ public AbstractMessage(MessageImplementor impl){ this.impl = impl; } /** * 发送消息,委派给实现部分的方法 * @param message 要发送消息的内容 * @param toUser 消息的接受者 */ public void sendMessage(String message , String toUser){ this.impl.send(message, toUser); } }
普通消息类
public class CommonMessage extends AbstractMessage { public CommonMessage(MessageImplementor impl) { super(impl); } @Override public void sendMessage(String message, String toUser) { // 对于普通消息,直接调用父类方法,发送消息即可 super.sendMessage(message, toUser); } }
加急消息类
public class UrgencyMessage extends AbstractMessage { public UrgencyMessage(MessageImplementor impl) { super(impl); } @Override public void sendMessage(String message, String toUser) { message = "加急:" + message; super.sendMessage(message, toUser); } /** * 扩展自己的新功能,监控某消息的处理状态 * @param messageId 被监控的消息编号 * @return 监控到的消息的处理状态 */ public Object watch(String messageId) { // 根据消息id获取消息的状态,组织成监控的数据对象,然后返回 return null; } }
实现发送消息的统一接口
public interface MessageImplementor { /** * 发送消息 * @param message 要发送消息的内容 * @param toUser 消息的接受者 */ public void send(String message , String toUser); }
系统内短消息的实现类
public class MessageSMS implements MessageImplementor { @Override public void send(String message, String toUser) { System.out.println("使用系统内短消息的方法,发送消息'"+message+"'给"+toUser); } }
邮件短消息的实现类
public class MessageEmail implements MessageImplementor { @Override public void send(String message, String toUser) { System.out.println("使用邮件短消息的方法,发送消息'"+message+"'给"+toUser); } }
客户端类
public class Client { public static void main(String[] args) { //创建具体的实现对象 MessageImplementor impl = new MessageSMS(); //创建普通消息对象 AbstractMessage message = new CommonMessage(impl); message.sendMessage("加班申请速批","李总"); //将实现方式切换成邮件,再次发送 impl = new MessageEmail(); //创建加急消息对象 message = new UrgencyMessage(impl); message.sendMessage("加班申请速批","李总"); } }
观察上面的例子会发现,采用桥梁模式来实现,抽象部分和实现部分分离开了,可以相互独立的变化,而不会相互影响。因此在抽象部分添加新的消息处理(特急消息),对发送消息的实现部分是没有影响的;反过来增加发送消息的方式(手机短消息),对消息处理部分也是没有影响的。
桥梁模式的优点
● 分离抽象和实现部分
桥梁模式分离了抽象部分和实现部分,从而极大地提供了系统的灵活性。让抽象部分和实现部分独立出来,分别定义接口,这有助于对系统进行分层,从而产生更好的结构化的系统。
● 更好的扩展性
桥梁模式使得抽象部分和实现部分可以分别独立地扩展,而不会相互影响,从而大大提高了系统的可扩展性。
九、适配器模式
适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本接口不匹配而无法在一起工作的两个类能够在一起工作。
类图:
Adapter类实现了Target接口,并继承自Adaptee,Adapter类的Request方法重新封装了Adaptee的SpecificRequest方法,实现了适配的目的。
因为Adapter与Adaptee是继承的关系,所以这决定了这个适配器模式是类的。
该适配器模式所涉及的角色包括:
目标(Target)角色:这是客户所期待的接口。源(Adaptee)角色:需要适配的类。适配器(Adapter)角色:把源接口转换成目标接口。这一角色必须是类。
适配器模式有2种,第一种是“面向类的适配器模式”,第二种是“面向对象的适配器模式”。
先说“面向类的适配器模式”。顾名思义,这类适配器模式就是主要用于,单一的为某个类而实现适配的这样一种模式,为什么说只为某个类去实现,一会提到,我们先展示这种类适配模式的代码实现。
源的代码如下:
[c-sharp]
view plain
copy
1. public class Person {
2.
3. private String name;
4. private String sex;
5. private int age;
6.
7. public void speakJapanese(){
8. out.println("I can speak Japanese!");
9. }
10.
11. public void speakEnglish(){
12. out.println("I can speak English!");
13. }
14. //以下省略成员变量的get和set方法
15. }
目标接口的代码如下:
[c-sharp]
view plain
copy
1. public interface Job {
2.
3. public abstract void speakJapanese();
4. public abstract void speakEnglish();
5. public abstract void speakFrench();
6.
7. }
适配器的代码如下:
[c-sharp]
view plain
copy
1. public class Adapter extends Person implements Job{
2.
3. public void speakFrench() {
4.
5. }
6.
7. }
好了,代码看完然后要做一些说明了,之前遗留的一个问题,为什么称其为类适配模式呢?很显然的,Adapter类继承了Person类,而在Java这种单继承的语言中也就意味着,他不可能再去继承其他的类了,这样也就是这个适配器只为Person这一个类服务。所以称其为类适配模式。
说完类的适配模式,我们要开始说第2种对象的适配器模式了。对象适配器模式是把“源”作为一个对象聚合到适配器类中。同样的话不多说,贴上代码:
源的代码以及目标代码同上,再次不再赘述。
仅贴出适配器代码:
[c-sharp]
view plain
copy
1. public class Adapter implements Job {
2.
3. Person person;
4.
5. public Adapter(Person person) {
6. this.person = person;
7. }
8.
9. public void speakEnglish() {
10. person.speakEnglish();
11. }
12.
13. public void speakJapanese() {
14. person.speakJapanese();
15. }
16.
17. //new add
18. public void speakFrench() {
19.
20. }
21.
22. }
对象的适配器模式,把“源”作为一个构造参数传入适配器,然后执行接口所要求的方法。这种适配模式可以为多个源进行适配。弥补了类适配模式的不足。
现在来对2种适配模式做个分析:
1.类的适配模式用于单一源的适配,由于它的源的单一话,代码实现不用写选择逻辑,很清晰;而对象的适配模式则可用于多源的适配,弥补了类适配模式的不足,使得原本用类适配模式需要写很多适配器的情况不复存在,弱点是,由于源的数目可以较多,所以具体的实现条件选择分支比较多,不太清晰。
2.适配器模式主要用于几种情况:(1)系统需要使用现有的类,但现有的类不完全符合需要。(2)讲彼此没有太大关联的类引进来一起完成某项工作(指对象适配)。
最后,再来顺带谈谈默认适配器模式:这种模式的核心归结如下:当你想实现一个接口但又不想实现所有接口方法,只想去实现一部分方法时,就用中默认的适配器模式,他的方法是在接口和具体实现类中添加一个抽象类,而用抽象类去空实现目标接口的所有方法。而具体的实现类只需要覆盖其需要完成的方法即可。代码如下:
接口类:
[c-sharp]
view plain
copy
1. public interface Job {
2.
3. public abstract void speakJapanese();
4. public abstract void speakEnglish();
5. public abstract void speakFrench();
6. public abstract void speakChinese();
7.
8. }
抽象类:
[c-sharp]
view plain
copy
1. public abstract class JobDefault implements Job{
2.
3. public void speakChinese() {
4.
5. }
6.
7. public void speakEnglish() {
8.
9. }
10.
11. public void speakFrench() {
12.
13. }
14.
15. public void speakJapanese() {
16.
17. }
18.
19. }
实现类:
[c-sharp]
view plain
copy
1. public class JobImpl extends JobDefault{
2.
3. public void speakChinese(){
4. out.println("I can speak Chinese!");
5. }
6.
7. }
转载于:https://blog.51cto.com/hyman1994/1663640