文章目录
JAVA设计模式
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。
一.设计模式的分类
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
其实还有两类:并发型模式和线程池模式。
二.设计模式的六大原则
设计模式的六大原则
开闭原则(Open Close Principle)
开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
里氏代换原则(Liskov Substitution Principle)
里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科
依赖倒转原则(Dependence Inversion Principle)
这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。
接口隔离原则(Interface Segregation Principle)
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。
迪米特法则(最少知道原则)(Demeter Principle)
为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
合成复用原则(Composite Reuse Principle)
原则是尽量使用合成/聚合的方式,而不是使用继承。
三.10种常用模式
1.单例模式
含义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
优点:
1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力。
3、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了
//1.简单单例
public class Singleton {
//持有私有静态实例,防止被引用
private static Singleton instance;
//构造方法用private,堵死了外界利用new创建此实例的可能
private Singleton(){
}
//次方法是获取本类实例的唯一全局访问点
public static Singleton getSingleton(){
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
//2.多线程的单例
//懒汉式1
//存在线程问题,需要进行双重锁定
public class Singleton {
private static Singleton instance;
private Singleton(){
}
public static Singleton getSingleton(){
if(instance == null){//没有实例才同步
synchronized (Singleton.class) {
if(instance == null){//解决都进入到同步块后,需要二次判断防止再次创建
instance = new Singleton();
}
}
}
return instance;
}
}
//3.懒汉式2
//这个singleton因为一直在内存中,所以它的内部类也一直在singleton里,直到虚拟机结束
public class Singleton {
//静态内部类
private static class SingletonHolder{
public static final Singleton instance = new Singleton();
}
private Singleton(){
}
public static Singleton getSingleton(){
return SingletonHolder.instance;
}
}
//4.饿汉式单例,不存在线程安全
//也就是静态初始化,在类加载初始化时就分配资源,有资源浪费问题
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){
}
public static Singleton getSingleton(){
return instance;
}
}
2.工厂模式
含义:专门负责将大量有共同接口的类实例化。工厂模式可以动态决定将哪一个类实例化,不必事先直到每次要实例化哪一个类。有三种形式。
简单工厂
简单工厂模式的最大优点在于工厂类 中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具 体产品的依赖。
但不符合开放-封闭原则
//操作抽象类
public abstract class Operation {
public double numberA;
public double numberB;
public abstract double result();
}
//加法类,乘除减等雷同
public class OperationAdd extends Operation {
@Override
public double result() {
return numberA + numberB;
}
}
//操作工厂类
public class OperationFactory {
public static Operation createOperation(char operator) {
Operation operation = null;
switch (operator) {
case '+':
operation = new OperationAdd();
break;
case '-':
operation = new OperationSub();
break;
case '*':
operation = new OperationMul();
break;
case '/':
operation = new OperationDiv();
break;
default:
throw new RuntimeException("unsupported operation");
}
return operation;
}
}
//计算类实现
public class Calculator {
public static void main(String[] args) {
Operation operation;
char operator;
operator = '+';
operation = OperationFactory.createOperation(operator);
operation.numberA = 1.2;
operation.numberB = 2.3;
System.out.println(operation.result());
}
}
工厂方法
工厂方法模式(Factory Method),定义一个用于创建对象的接口,让子类决定实例化哪一 个类。工厂方法使一个类的实例化延迟到其子类。
其实你仔细观察就会发现,工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,也就是说,工厂方法把简单工厂的内部逻辑判断移到了客户端代码来 进行。你想要加功能,本来是改工厂类的,而现在是修改客户端!
工厂方法克服了简单工厂违背开放-封闭原则的缺点,又保持了封装对象创建过程的优点
工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。但缺点是由于每加一个产品,就 需要加一个产品工厂的类,增加了额外的开发量。
//工厂接口
public interface IFactory {
public Operation createOperation();
}
//加法工厂实现
class AddFactory implements IFactory {
@Override
public Operation createOperation() {
return new OperationAdd();
}
}
//客户端调用
public class FactoryClient {
public static void main(String[] args) {
IFactory operFactory = new DivFactory();
Operation operation = operFactory.createOperation();
operation.numberA = 3.4;
operation.numberB = 4.5;
System.out.println(operation.result());
}
}
抽象工厂
抽象工厂模式(Abstract Factory),提供一个创建一系列相关 或相互依赖对象的接口,而无需指定它们具体的类。
最大的好处便是易于交换产品系列,由于具体工厂类,例如IFactory factory=new AccessFactory0, 在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只 需要改变具体工厂即可使用不同的产品配置。我们的设计不能去防止需求的更改,那么我们的理想便是 让改动变得最小,现在如果你要更改数据库访问,我们只需要更改具体工厂就可以做到。
第二大好处是, 它让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被 具体工厂的实现分离,不会出现在客户代码中。
//利用反射改造简单工厂模式,去掉分支判断的逻辑
public class OperationFactory {
private static Map<String, Class<?>> allOperationMaps = new HashMap<String, Class<?>>();
//下面是模拟不同产品实现类
public static void fillMap() {
allOperationMaps.put("+", OperationAdd.class);
allOperationMaps.put("-", OperationSub.class);
allOperationMaps.put("*", OperationMul.class);
allOperationMaps.put("/", OperationDiv.class);
}
public static Operation createOperation(String operator)
throws InstantiationException, IllegalAccessException {
Operation operation;
fillMap();
Class<?> operationClass = allOperationMaps.get(operator);
if (operationClass == null) {
throw new RuntimeException("unsupported operation");
}
operation = (Operation) operationClass.newInstance();
return operation;
}
}
//客户端
public class Client {
public static void main(String[] args) throws InstantiationException,
IllegalAccessException {
Operation operation = OperationFactory.createOperation("/");
operation.numberA = 7;
operation.numberB = 8;
System.out.println(operation.result());
}
}
3.装饰模式
含义:装饰模式(Decorator),动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活
意义:把类中的装饰功能从类中搬移去除,这样可以简化原有的类 ,把类中的装饰功能从类中搬移去除,这样可以简化原有的类
继承+组合:装饰模式的Decorator类很重要,一方面继承自抽象类Component,另一方面在Decorator抽象类中组合Component对象,通过set方法将相应的Component对象赋值给Decorator中组合的Component对象,Decorator中的operation()操作继承自Component,并且它的方法体内容为:如果Decorator中组合的Component对象非空,执行该Component对象的operation()方法。各个具体的Decorator类中的operation()方法都会先调用父类的operation()方法,以使得Decorator中的operation()操作得以执行,达到层层包装的效果
起初的设计中,当系统需要新功能的时候,是向旧的类中添加新的代码。这些新加的代码通常装饰了原有类的核心职责或主要行为,比如用西装或嘻哈服来装饰小菜,但这种做法的问题在于,它们在主类中加入了新的字段,新的方法和新的逻辑,从而增加了主类的复杂度,就像你起初的那个‘人’类,而这些新加入的东西仅仅是为了满足一些只在某种特定情况下才会执 行的特殊行为的需要。而装饰模式却提供了一个非常好的解决方案,它把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,因此,当需要执行特殊行为时,客户代码就可以在运行时根据需要有选择地、按顺序地使用装饰功能包装对象了。
//Component是定义一个对象接口,可以给这些对象动态地添加职责
public abstract class Component {
public abstract void operation();
}
//ConcreteComponent是定义一个具体的对象,也可以给这个对象添加一些职责
public class ConcreteComponent extends Component {
@Override
public void operation() {
System.out.println("具体对象的操作");
}
}
//Decorator,装饰抽象类,继承了Component,从外类来扩展Component类的功能,但对于Component来说,
//是无需知道Decorator的存在的
public abstract class Decorator extends Component {
protected Component component;//用于接收上一个相同类型的对象
public Component getComponent() {
return component;
}
public void setComponent(Component component) {
this.component = component;
}
@Override
public void operation() {
if (component != null) {
component.operation();
}
}
}
class ConcreteDecoratorA extends Decorator {
private String addedState;
@Override
public void operation() {
// 首先运行原Component的operation(),再执行本类的功能,如addedState,相当于对原Component进行了装饰
super.operation();
addedState = "A中的new state ";
System.out.println(addedState + "具体装饰对象A的操作");
}
}
class ConcreteDecoratorB extends Decorator {
@Override
public void operation() {
super.operation();
addedBehavior();
System.out.println("具体装饰对象B的操作");
}
public void addedBehavior() {
System.out.print("B中的新增行为 ");
}
}
class ConcreteDecoratorC extends Decorator {
@Override
public void operation() {
super.operation();
System.out.println("C没有特殊行为 " + "具体装饰对象C的操作");
}
}
//装饰模式客户端调用代码,装饰的过程更像是层层包装,用前面的对象装饰后面的对象
public class DecoratorClient {
public static void main(String[] args) {
ConcreteComponent concreteComponent = new ConcreteComponent();
ConcreteDecoratorA concreteDecoratorA = new ConcreteDecoratorA();
ConcreteDecoratorB concreteDecoratorB = new ConcreteDecoratorB();
ConcreteDecoratorC concreteDecoratorC = new ConcreteDecoratorC();
concreteDecoratorA.setComponent(concreteComponent);
concreteDecoratorB.setComponent(concreteDecoratorA);
concreteDecoratorC.setComponent(concreteDecoratorB);
concreteDecoratorC.operation();
}
}
4.适配器模式
含义:适配器模式(Adapter),将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
意义:在软件开发中,也就是系统的数据和行为都正确,但接口不符时,我们应该考虑用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环境要求不一致的情况,比如在需要对早期代码复用一些功能等应用上很有实际价值。类适配器模式和对象适配器模式,由于类适配器模式通过多重继承对一个接口与另一个接口进行匹配,而C#、VB.NET、JAVA等语言都不支持多重继承(C++支持),也就是一个类只有一个父类,所以我们这里主要讲的是对象适配器
在想使用一个已经存在的类,但如果它的接口,也就是它的方法和你的要求不相同时,就应该考虑用适配器模式。两个类所做的事情相同或相似,但是具有不同的接口时要使用它。而且由于类都共享同一个接口,使得客户代码可以统一调用同一接口就行了,这样应该可以更简单、更直接、更紧凑。在双方都不太容易修改的时候,再使用适配器模式适配
//客户所期待的接口
public abstract class Target {
//客户正常的行为request
public void request() {
System.out.println("普通请求!");
}
}
//需要适配的类
public class Adaptee {
//一些特殊的功能行为
public void specificRequest() {
System.out.println("特殊的请求!");
}
}
//适配器类,通过在内部包装一个Adaptee对象,把原接口转换成目标接口
public class Adapter extends Target {
private Adaptee adaptee = new Adaptee();
@Override
public void request() {//客户行为对外不变,只不过内部实现改变成其他方式来实现
//复用特殊的功能
adaptee.specificRequest();
}
}
//适配器客户端
public class AdapterClient {
public static void main(String[] args) {
Target target;
target = new Adapter();
target.request();
}
}
5.外观模式(门面模式)
含义:外观模式(Facade),为子系统中的一组接口提供一个一致的界面,此模 式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
意义:完美体现了依赖倒转原则和迪米特法则的思想,最常用模式之一
这要分三个阶段来说,首先,在设计初期阶段,应该要有意识的将不同的两个层分离,比如经典 的三层架构,就需要考虑在数据访问层和业务逻辑层、业务逻辑层和表示层的层与层之间建立外观 Facade,这样可以为复杂的子系统提供一个简单的接口,使得耦合大大降低。
其次,在开发阶段,子系 统往往因为不断的重构演化而变得越来越复杂,大多数的模式使用时也都会产生很多很小的类,这本是 好事,但也给外部调用它们的用户程序带来了使用上的困难,增加外观Facade可以提供一个简单的接 口,减少它们之间的依赖。
第三,在维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩 展了,但因为它包含非常重要的功能,新的需求开发必须要依赖于它。此时用外观模式Facade也是非常 合适的。你可以为新系统开发一个外观Facade类,来提供设计粗糙或高度复杂的遗留代码的比较清晰 简单的接口,让新系统与Facade对象交互,Facade与遗留代码交互所有复杂的工作
//“系统”接口,只是标记接口,暂无任何意义,
public interface SystemInterface {
}
//子系统类,这4个可以不实现接口,接口可能是后续扩展多态之类而使用的
class SubSystemOne implements SystemInterface {
public void methodOne() {
System.out.println("子系统方法一");
}
}
class SubSystemTwo implements SystemInterface {
public void methodTwo() {
System.out.println("子系统方法二");
}
}
class SubSystemThree implements SystemInterface {
public void methodThree() {
System.out.println("子系统方法三");
}
}
class SubSystemFour implements SystemInterface {
public void methodFour() {
System.out.println("子系统方法四");
}
}
//外观类,它需要了解所有的子系统的方法或属性,进行组合,以备外界调用
public class Facade {
SubSystemOne subSystemOne;
SubSystemTwo subSystemTwo;
SubSystemThree subSystemThree;
SubSystemFour subSystemFour;
public Facade() {
subSystemOne = new SubSystemOne();
subSystemTwo = new SubSystemTwo();
subSystemThree = new SubSystemThree();
subSystemFour = new SubSystemFour();
}
public void methodA() {
System.out.println("方法组A:");
subSystemOne.methodOne();
subSystemTwo.methodTwo();
subSystemFour.methodFour();
}
public void methodB() {
System.out.println("方法组B:");
subSystemThree.methodThree();
subSystemFour.methodFour();
}
}
//外观类客户端
public class FacadeClient {
public static void main(String[] args) {
// 由于Facade的作用,客户端可以根本不知道四个子系统的存在
// 启发:维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展了,此时可以
// 为新系统开发一个外观Facade类,来提供设计粗糙或高度复杂的遗留代码的比较清晰简单
// 的接口,让新系统与Facade对象交互,Facade与遗留代码交互所有复杂的工作
Facade facade = new Facade();
facade.methodA();
facade.methodB();
}
}
6.代理模式
含义:代理模式(Proxy),为其他对象提供一种代理以控制对这个对象的访问。
意义:
一般来说分为几种,第一,远程代理,也就是为一个对象在不同的地址空间提供局部代表。这样 可以隐藏一个对象存在于不同地址空间的事实。WebService在.NET中的应用是怎么做的 ? 当我在应用程序的项目中加入一个Web引用,引用一个WebService, 此时会在项目中生成一个WebReference的文件夹和一些文件,其实它们就是代理,这就使得客户端程序 调用代理就可以解决远程访问的问题。
第二种应用是虚拟代理,是根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的 真实对象。这样就可以达到性能的最优化,比如说你打开一个很大的HTML网页时,里面可能有很 多的文字和图片,但你还是可以很快打开它,此时你所看到的是所有的文字,但图片却是一张一张地下 载后才能看到。那些未打开的图片框,就是通过虚拟代理来替代了真实的图片,此时代理存储了真实图 片的路径和尺寸。
第三种应用是安全代理,用来控制真实对象访问时的权限。一般用于对象应该有不同的访问权 限的时候。
第四种是智能指引,是指当调用真实的对象时,代理处理另外一些事。如计算真实对象的引用次数,这样当该对象没有引用时,可以自动释放它;或当第一次引用一个持久对象时,将它装入内存;或在访问一个实际对象前,检查是否已经锁定它,以确保其他对象不能改变它。它们都是通过代 理在访问一个对象时附加一些内务处理。
//定义真实实体类与代理类共用的接口
public interface Subject {
public void request();
}
//真实实体类
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("真实对象的请求");
}
}
//代理类
public class Proxy implements Subject {
// 保存一个引用,使得代理可以访问真实实体
Subject subject;
public Proxy() {
subject = new RealSubject();
}
@Override
public void request() {
subject.request();
}
}
//代理客户端
public class ProxyClient {
public static void main(String[] args) {
Proxy proxy = new Proxy();
proxy.request();
}
}
7.策略模式
含义:策略模式(Strategy):它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
意义:策略模式是一种定义一系列算法的方法,从概念上来看,所有这些 算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类 与使用算法类之间的耦合。
策略模式的Strategy类层次为Context定义了一系列的可供重用的算法或行为。继承有助于析取 出这些算法中的公共功能。
另外一个策略模式的优点是简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试[DPE]。” “每个算法可保证它没有错误,修改其中任一个时也不会影响其他的算法。这真的是非常好。
当不同的行为堆砌在一个类中时,就很难避免使用条件语句来选择合适的行为。将这些行为封装在一个个独立的Strategy类中,可以在使用这些行为的类中消除条件语句。就商场收银系统的例子而言,在 客户端的代码中就消除条件语句,避免了大量的判断。这是非常重要的进展。策略模式封装了变化,策略模式就是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理 这种变化的可能性。 感觉在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的 Context对象。这本身并没有解除客户端需要选择判断的压力,而策略模式与简单工厂模式结合后, 选择具体实现的职责也可以由Context来承担,这就最大化地减轻了客户端的职责。后续还可以使用反射来优化。
//策略接口
public interface Strategy {
public void algorithmInterface();
}
//具体策略
class ConcreteStrategyA implements Strategy {
@Override
public void algorithmInterface() {
System.out.println("策略A的具体算法实现");
}
}
class ConcreteStrategyB implements Strategy {
@Override
public void algorithmInterface() {
System.out.println("策略B的具体算法实现");
}
}
class ConcreteStrategyC implements Strategy {
@Override
public void algorithmInterface() {
System.out.println("策略C的具体算法实现");
}
}
//上下文
public class Context {
Strategy strategy;
//初始化时,传入具体策略类
public Context(Strategy strategy) {
this.strategy = strategy;
}
//根据具体策略对象,调用其相应策略方法
public void contextInterface() {
strategy.algorithmInterface();
}
}
//客户端使用策略
public class StrategyClient {
public static void main(String[] args) {
Context context;
context = new Context(new ConcreteStrategyA());
context.contextInterface();
context = new Context(new ConcreteStrategyB());
context.contextInterface();
context = new Context(new ConcreteStrategyC());
context.contextInterface();
}
8.观察者模式(发布-订阅模式)
含义:观察者模式定义了一种一对多的依赖关系,让多个观察者对象 同时监听某一个主题对象。这个主题对象在状态发生变化时, 会通知所有观察者对象,使它们能够自动更新自己 。
意义:
将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。而观察者模式的关键对象是主题Subject和观察者Observer,一个Subject可以有任意数目的依赖 它的Observer,一旦Subject的状态发生了改变,所有的Observer都可以得到通知。Subject发出通知时 并不需要知道谁是它的观察者,也就是说,具体观察者是谁,它根本不需要知道。而任何一个具体观察者不知道也不需要知道其他观察者的存在。
当一个对象的改变需要同时改变其他对象的时候,我们就可以使用这个模式。而且它不知道具体有多少对象有待改变时,应该考虑使用观察者模式。
当一个抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。总的来讲,观察者模式所做的工作其实就是在解除耦合。让耦合的双方都依赖于抽象, 而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化
尽管已经用了依赖倒转原则,但是‘抽象通知者’还是依赖‘抽象观察 者’,也就是说,万一没有了抽象观察者这样的接口,我这通知的功能就完不成了。另外就是每个具体观 察者,它不一定是‘更新’的方法要调用呀,后续可以使用委托方式来进一步优化。
//主题或抽象通知者
public abstract class Subject {
private List<Observer> observers = new ArrayList<Observer>();
public void attach(Observer observer) {
observers.add(observer);
}
public void detach(Observer observer) {
observers.remove(observer);
}
public void notifyObserver() {
for (Observer observer : observers) {
observer.update();
}
}
}
//具体主题或通知者
public class ConcreteSubject extends Subject {
private String subjectState;//具体被观察者状态
public String getSubjectState() {
return subjectState;
}
public void setSubjectState(String subjectState) {
this.subjectState = subjectState;
}
}
//抽象观察者
public abstract class Observer {
public abstract void update();
}
//具体观察者
public class ConcreteObserver extends Observer {
private String name;
private String observerState;
private ConcreteSubject concreteSubject;
public ConcreteObserver(ConcreteSubject concreteSubject, String name) {
this.setName(name);
this.setConcreteSubject(concreteSubject);
}
@Override
public void update() {
this.setObserverState(concreteSubject.getSubjectState());
System.out.println("观察者" + this.getName() + "的新状态是"
+ this.getObserverState());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getObserverState() {
return observerState;
}
public void setObserverState(String observerState) {
this.observerState = observerState;
}
public ConcreteSubject getConcreteSubject() {
return concreteSubject;
}
public void setConcreteSubject(ConcreteSubject concreteSubject) {
this.concreteSubject = concreteSubject;
}
}
//观察者模式客户端代码
public class ObserverClient {
public static void main(String[] args) {
ConcreteSubject concreteSubject = new ConcreteSubject();
concreteSubject.attach(new ConcreteObserver(concreteSubject, "X"));
concreteSubject.attach(new ConcreteObserver(concreteSubject, "Y"));
concreteSubject.attach(new ConcreteObserver(concreteSubject, "Z"));
concreteSubject.setSubjectState("ABC");
concreteSubject.notifyObserver();
}
}
9.模板方法模式
含义:模板方法模式,定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
意义:
模板方法模式是通过把不变行为搬移到超类,去除子类中的重复代码来体现它的优势,模板方法模式就是提供了一个很好的代码复用平台。因为有时候,我们会遇到由一系列步骤构成的过程需要执行。这个过程从高层次上看是相同的,但有些步骤的实现可能不同。这时候,我们通常就应该要考虑用模板方法模式了。
当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。我们通过模板方法模式把这些行为搬移到单一的地方,这样就 帮助子类摆脱重复的不变行为的纠缠。模板方法模式是很常用的模式,对继承和多态玩得好的人几乎都会在继承体系中多多少少用到它。比如在.NET或Java类库的设计中,通常都会利用模板方法模式提取类库中的公共行为到抽象类中。
//模板方法抽象类:不变的部分给出具体实现,变化的部分封装为抽象方法延迟到子类实现
public abstract class AbstractTemplate {
//一些抽象的行为,放到子类中去实现
public abstract void primitiveOperation1();
public abstract void primitiveOperation2();
//模板方法,给出了逻辑的骨架,而逻辑的组成是一些相应的抽象操作,推迟到子类中去实现
public void templateMethod() {
primitiveOperation1();
primitiveOperation2();
System.out.println("模板方法结束\n");
}
}
//具体类A
public class ConcreteClassA extends AbstractTemplate {
@Override
public void primitiveOperation1() {
System.out.println("具体类A的方法1实现");
}
@Override
public void primitiveOperation2() {
System.out.println("具体类A的方法2实现");
}
}
//具体类B
public class ConcreteClassB extends AbstractTemplate {
@Override
public void primitiveOperation1() {
System.out.println("具体类B的方法1实现");
}
@Override
public void primitiveOperation2() {
System.out.println("具体类B的方法2实现");
}
}
//模板方法客户端
public class TemplateClient {
public static void main(String[] args) {
AbstractTemplate abstractTemplate;
abstractTemplate = new ConcreteClassA();
abstractTemplate.templateMethod();
abstractTemplate = new ConcreteClassB();
abstractTemplate.templateMethod();
}
}
10.命令模式
含义:命令模式(Command),将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
意义:
第一,它能较容易地设计一个命令队列;
第二,在需要的情况下,可以较容易地将命令记入日志;
第三,允许接收请求的一方决定是否要否决请求。
第四,可以容易地实现对请求的撤销和重做;
第五,由于加进新的具体命令类不影响其 他的类,因此增加新的具体命令类很容易。其实还有最关键的优点就是命令模式把请求一个操作的对象 与知道怎么执行一个操作的对象分割开。
但是否是碰到类似情况就一定要实现命令模式呢?,这就不一定了,比如命令模式支持撤销/恢复操作功能,但你还不清楚是否需要这个功能时,你要不要实现命令模式?其实应该是不要实现。敏捷开发原则告诉我们,不要为代码添加基于猜测的、实际不需要的功能。 如果不清楚一个系统是否需要命令模式,一般就不要着急去实现它,事实上,在需要的时候通过重构实现这个模式并不困难,只有在真正需要如撤销/恢复操作等功能时,把原来的代码重构为命令模式才有意义。
public abstract class Command {
protected List<Reciever> recievers;
public Command(List<Reciever> recievers) {
this.recievers = recievers;
}
public void addRecievers(Reciever reciever) {
this.recievers.add(reciever);
}
public abstract void execute();
}
// 将一个接收者对象绑定于一个动作,调用接收者相应的操作,以实现execute
class ConcreteCommand extends Command {
public ConcreteCommand(List<Reciever> recievers) {
super(recievers);
}
@Override
public void execute() {
for (Reciever reciever : recievers) {
reciever.action();
}
}
}
//知道如何实施与执行一个与请求相关的操作,任何类都可能作为一个接收者。真正执行请求的地方!
interface Reciever {
public void action();
}
class RecieverA implements Reciever {
@Override
public void action() {
System.out.println("RecieverA执行请求!");
}
}
class RecieverB implements Reciever {
@Override
public void action() {
System.out.println("RecieverB执行请求!");
}
}
class RecieverC implements Reciever {
@Override
public void action() {
System.out.println("RecieverC执行请求!");
}
}
//要求该命令执行这个请求
public class Invoker {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void executeCommand() {
command.execute();
}
}
//创建一个具体命令对象并设定它的接收者
public class CommandClient {
public static void main(String[] args) {
List<Reciever> recievers = new ArrayList<Reciever>();
recievers.add(new RecieverA());
recievers.add(new RecieverB());
recievers.add(new RecieverC());
Command command = new ConcreteCommand(recievers);
Invoker invoker = new Invoker();
invoker.setCommand(command);
invoker.executeCommand();
}
}
11.状态模式
含义:状态模式(State),当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
意义:
状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断 逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。当然,如果这个状态判断很简 单,那就没必要用‘状态模式’了。
状态模式的好处是将与特定状态相关的行为局部化,并且将不同状态的行为分割开来
将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某 个ConcreteState中,所以通过定义新的子类可以很容易地增加新的状态和转换,这样做的目的就是为了消除庞大的条件分支语句,大的分支判断会使得它们难以修改和扩展,就像我们最早说的刻版印刷一样,任何改动和变化都是致命的。状态模式通过把各种状态转移逻 辑分布到State的子类之间,来减少相互间的依赖,好比把整个版面改成了一个又一个的活字,此时就 容易维护和扩展了。
当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考 虑使用状态模式了。另外如果业务需求某项业务有多个状态,通常都是一些枚举常量,状态的变化都是 依靠大量的多分支判断语句来实现,此时应该考虑将每一种业务状态定义为一个State的子类。这样这些 对象就可以不依赖于其他对象而独立变化了,某一天客户需要更改需求,增加或减少业务状态或改变状 态流程,对你来说都是不困难的事
//Context类,维护一个ConcreteState子类的实例,这个实例定义当前的状态
public class Context {
//可读写的状态属性,用于读取当前状态和设置新状态
private State state;
//定义context的初始状态
public Context(State state) {
this.state = state;
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
}
//对请求做处理,并设置下一状态
public void request() {
this.state.handle(this);
}
}
//抽象状态类
public abstract class State {
public abstract void handle(Context context);
}
class ConcreteStateA extends State {
@Override
public void handle(Context context) {//设置conA的下一状态是conB
System.out.println("现在是在状态A");
context.setState(new ConcreteStateB());
}
}
class ConcreteStateB extends State {
@Override
public void handle(Context context) {
System.out.println("现在是在状态B");
context.setState(new ConcreteStateC());
}
}
class ConcreteStateC extends State {
@Override
public void handle(Context context) {
System.out.println("现在是在状态C");
context.setState(new ConcreteStateA());
}
}
//客户端:不断请求,不断更改状态
public class StateClient {
public static void main(String[] args) {
Context context = new Context(new ConcreteStateA());
//不断的请求,同时更改状态
context.request();
context.request();
context.request();
context.request();
context.request();
}
}
参考网址:
书籍:大话设计模式,JAVA与模式