《设计模式之禅》 -- 读后笔记

设计模式六原则

单一职责原则

定义:应该有且仅有一个原因引起类的变更,也就是说一个接口或者抽象类,甚至是普通类的设计,应该尽可能的将不同因素的数据或者行为分开,如果之后有什么新的因素需要添加或者修改接口,只需要修改对于类别的接口,这样就尽可能的减少了一个接口修改的频率,而且也减少了对其他类影响的概率;但是在现实中使用,是需要看项目,看实际情况酌情选择的,因为过分细分也会人为导致系统复杂度提高。

里氏替换原则

定义:一句话就是当父类出现的地方,替换成子类不会出现任何报错或异常;如果当一个子类不能完整的实现父类的方法,或者子类的方法里实现的作用改变了,这种最好就是断开子类和父类的关系,采用依赖、聚集、组合的关系。

依赖倒置原则

定义:就是两个功能模块之间尽量依赖抽象,不要依赖细节,而抽象不依赖细节,让细节依赖抽象;

接口隔离原则

定义:类间的依赖关系应该建立在最小的接口上,客户端不应该依赖它不需要的接口,一句话就是,建立单一接口,不要建立臃肿庞大的接口。

迪米特法则

定义:也叫最少知识原则,该原则提倡尽量减少对外开放的方法,保持着类之间的高内聚,这样后期修改代码影响的范围就越小,让类之间的耦合减弱,但是这样也会带来系统复杂度的提高。

开闭原则

定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭,通俗的话讲,就是设计的代码在未来修改时尽量可以使用扩展类的方式,而不是必须修改类的代码方式完成,开闭原则是最基础的一个原则,其它原则都是指导设计的工具和方法,而开闭原则才是其精神领袖。

总结

其实设计原则只是指导设计的理念,如何设计以及如何进行取舍需要按照实际情况,对项目进行自己的评判,一句话就是尽量依赖抽象契约,或者使用元数据控制(配置参数),对感觉会出现变化的点,应该抽象出来。

23种设计模式

单例模式

定义:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例;单例模式是一个比较简单的模式,通过将构造函数私有化,避免其它类创建新的对象,然后将该对象在类内部自动实例化,通过静态的方式在类加载的时候直接实例化,自动实例化的时间点不同,也区分为饿汉式和懒汉式,需要注意的是懒汉式会出现线程不安全的情况,而且Cloneable的clone方法复制对象是不经过构造函数的,所以该情况也需要注意。

public class Singleton {
	private static final Singleton singleton = new Singleton();
	//限制产生多个对象
	private Singleton(){
	}
	//通过该方法获得实例对象
	public static Singleton getSingleton(){
		return singleton;
	}
	//类中其他方法,尽量是static
	public static void doSomething(){
	}
}

优点: 单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显,可以避免对资源的多重占用,例如一个写文件动作,由于只有一个实例存在
内存中,避免对同一个资源文件的同时写操作。
缺点: 单例模式一般没有接口,扩展很困难。

工厂方法模式

定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类,该模式也是经常使用的且简单的模式,如果我们将抽象的工厂类去掉,只使用具体工厂类实现调用,这种就叫做简单工厂模式。

// 抽象创建工厂类
public abstract class Creator {
/*
* 创建一个产品对象,其输入参数类型可以自行设置
* 通常为String、Enum、Class等,当然也可以为空
*/
	public abstract <T extends Product> T createProduct(Class<T> c);
}

// 具体创建工厂类
public class ConcreteCreator extends Creator {
	public <T extends Product> T createProduct(Class<T> c){
		Product product=null;
		try {
			product = (Product)Class.forName(c.getName()).newInstance();
		} catch (Exception e) {
		//异常处理
		}
		return (T)product;
	}
}

优点: 良好的封装性,代码结构清晰,一个调用者需要一个具体的产品对象,只要知道这个产品的类名(或约束字符串)就可以了,不用知道创建对象的艰辛过程,降低模块间的耦合,工厂方法模式的扩展性非常优秀,我们可以在很多地方看到,例如:单例工厂类,延迟加载(我们通过将工厂创建的类缓存着,等待下一次复用)。

抽象工厂模式

定义:为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们的具体类,抽象工厂就是工厂模式的改造,将抽象工厂的具体工厂类和产品类对应起来,将每个工厂的职责单一化。

优点: 封装性非常好,想要获得什么产品就直接使用对应的工厂类即可。
缺点: 虽然封装性很好,使用简单,但是会导致扩展性很难,增加一个产品类就需要修改抽象工厂,增加对应工厂类。

模板方法模式

定义:定义一个操作中的算法的框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤,通俗的讲,就是我们在抽象类里定义了一个方法,该方法是抽象方法的执行流程,然后将抽象方法延迟到子类种定义,最后执行的时候就会实现调用同一个方法,根据不同的子类覆盖的代码不同,呈现不同的效果。

// 这样子类只需要实现基本方法,然后调用者统一调用模板方法完成不同的功能
// 当然如果想要让模板方法里的执行根据不同情况做出不同处理,就需要添加钩子函数让用户定义返回值,然后模板函数根据这个返回值做出处理。
public abstract class AbstractClass {
	//基本方法
	protected abstract void doSomething();
	//基本方法
	protected abstract void doAnything();
	//模板方法
	public void templateMethod(){
		/*
		* 调用基本方法,完成相关的逻辑
		*/
		this.doAnything();
		this.doSomething();
	}
}

优点: 封装不变部分,扩展可变部分,提取公共部分代码,便于维护,行为由父类控制,子类实现。
缺点: 可读性降低,会导致提高新手代码阅读的难度。

建造者模式

定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示;举个例子,所有的车都是有最基本的构造,只是每个具体的车在零件上面的表达不同,我们让产品类获取一个用户自定义的集合,然后抽象一个构建者,将车的零件集合给产品类,这样我们可以通过自己的想法构建一个具体的子类,构建出不同的车子,也可以通过一个导演类,去预先设定一个顺序保存,这样我们就可通过导演类很方便的构建出一个我们需要的对象。

// 产品类,也可以是一个抽象类
public class Product {
	public void doSomething(){
	//独立业务处理
	}
}

//  抽象建造者
public abstract class Builder {
	//设置产品的不同部分,以获得不同的产品
	public abstract void setPart();
	//建造产品
	public abstract Product buildProduct();
}

//  具体建造者
public class ConcreteProduct extends Builder {
	private Product product = new Product();
	//设置产品零件
	public void setPart(){
	/*
	* 产品类内的逻辑处理
	*/
	}
	//组建一个产品
	public Product buildProduct() {
	return product;
	}
}

优点: 封装性高,建造者独立,容易扩展,便于控制细节风险,新增一个建造者不会对其它代码造成任何影响,注重组合和顺序,和工厂方法有些许不同,适合那种用户自定义组合或者顺序的处理。

代理模式

定义:为其他对象提供一种代理以控制对这个对象的访问,也就是将客户端调用实际角色类的一系列行为交给代理人对象去执行,而且代理人还可以在执行的前后增加逻辑处理;还有一种就是强制代理的方式,要访问真实角色类必须通过该类返回的代理类访问,如果是其它自己创建的代理类或者真实角色类就不能访问,这个就需要在真实访问类里添加创建代理判断并且返回的功能。

//  通过实现角色抽象类,可以像使用真实角色类一样的使用
public class GamePlayerProxy implements IGamePlayer {
    //  代理的角色类
	private IGamePlayer gamePlayer = null;
	//通过构造函数传递要对谁进行代练
	public GamePlayerProxy(String name){
		try {
			gamePlayer = new GamePlayer(this,name);
		} catch (Exception e) {
		// TODO 异常处理
		}
	}
	//代练杀怪
	public void killBoss() {
		this.gamePlayer.killBoss();
	}
	//代练登录
	public void login(String user, String password) {
		this.gamePlayer.login(user, password);
	}
	//代练升级
	public void upgrade() {
		this.gamePlayer.upgrade();
	}
}

优点: 职责清晰,高扩展性,智能化。
特例: 动态代理,也就是将代理类里的被代理类对象换成实现invocationhandle接口的增强对象,然后通过该增强对象实现方法的增强,代理类直接使用该增强对象的invoke()方法实现。

原型模式

定义:使用一个已创建的对象,直接复制出另一个对象,在java里实现Cloneable接口,覆写clone()方法;该模式非常简单。

public class Thing implements Cloneable{
	//定义一个私有变量
	private  ArrayList<String> arrayList = new ArrayList<String>();
	@Override
	public Thing clone(){
		Thing thing=null;
		try {
			thing = (Thing)super.clone();
			this.arrayList = (ArrayList<String>)this.arrayList.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return thing;
	}
}

优点: 因为是直接在内存里通过二进制复制数据到堆里,能节省资源消耗,io消耗。
缺点: 因为是直接复制数据的方式,所以不会经过虚拟机栈的执行,也就是构造方法的执行,所以只能针对某些特殊情况使用,而且该方式只能使用浅拷贝,对于引用类型的属性只是复制了指针,如果要实现深拷贝,需要手动在clone方法里处理,或者实现Serializable接口,通过序列化和反序列化的方式另类新建一个同样的对象。

中介者模式

定义:用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变它们之间的交互;也就是创建一个中介者类,然后让中介者去依赖各个同事类,在中介者内部实现各个同事相互间的逻辑处理,而每个同事类在想要调用其他同事类的方法时,通过中介者类可以实现,这种模式能够让网状关系变为星状关系。

//  抽象的中介者类
public abstract class Mediator {
	//定义同事类
	protected ConcreteColleague1 c1;
	protected ConcreteColleague2 c2;
	//通过getter/setter方法把同事类注入进来
	public ConcreteColleague1 getC1() {
		return c1;
	}
	public void setC1(ConcreteColleague1 c1) {
		this.c1 = c1;
	}
	public ConcreteColleague2 getC2() {
		return c2;
	}
	public void setC2(ConcreteColleague2 c2) {
		this.c2 = c2;
	}
	//中介者模式的业务逻辑
	public abstract void doSomething1();
	public abstract void doSomething2();
}

// 具体中介者类
public abstract class Mediator {
	//定义同事类
	protected ConcreteColleague1 c1;
	protected ConcreteColleague2 c2;
	//通过getter/setter方法把同事类注入进来
	public ConcreteColleague1 getC1() {
		return c1;
	}
	public void setC1(ConcreteColleague1 c1) {
		this.c1 = c1;
	}
	public ConcreteColleague2 getC2() {
		return c2;
	}
	public void setC2(ConcreteColleague2 c2) {
		this.c2 = c2;
	}
	//中介者模式的业务逻辑
	public abstract void doSomething1();
	public abstract void doSomething2();
}

// 抽象同事类
public abstract class Colleague {
	protected Mediator mediator;
	public Colleague(Mediator _mediator){
	this.mediator = _mediator;
	}
}

//  具体同事类
public class ConcreteColleague1 extends Colleague {
	//通过构造函数传递中介者
	public ConcreteColleague1(Mediator _mediator){
		super(_mediator);
	}
	//自有方法 self-method
	public void selfMethod1(){
		//处理自己的业务逻辑
	}
	//依赖方法 dep-method
	public void depMethod1(){
		//处理自己的业务逻辑
		//自己不能处理的业务逻辑,委托给中介者处理
		super.mediator.doSomething1();
	}
}

优点: 中介者模式的优点就是减少类间的依赖,把原有的一对多的依赖变成了一对一的依赖,同事类只依赖中介者。
缺点: 中介者模式的缺点就是中介者会膨胀得很大,而且逻辑复杂,原本N个对象直接的相互依赖关系转换为中介者和同事类的依赖关系,同事类越多,中介者的逻辑就越复杂。

命令模式

定义:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能;将一系列通用指令抽象出来,具体指令类实现相应的逻辑,而调用者只需要接受一个具体指令类执行即可。

public abstract class Receiver {
	//抽象接收者,定义每个接收者都必须完成的业务,也就是指令类需要调用的业务抽象类
	public abstract void doSomething();
}

public class ConcreteReciver1 extends Receiver{
	//每个接收者都必须处理一定的业务逻辑,也就是指令类需要调用的具体业务类
	public void doSomething(){
	}
}

public abstract class Command {
	//每个命令类都必须有一个执行命令的方法
	public abstract void execute();
}

public class ConcreteCommand1 extends Command {
	//对哪个Receiver类进行命令处理
	private Receiver receiver;
	//构造函数传递接收者
	public ConcreteCommand1(Receiver _receiver){
		this.receiver = _receiver;
	}
	//必须实现一个命令
	public void execute() {
		//业务处理
		this.receiver.doSomething();
	}
}

public class Invoker {
	//  接受指令,调用指令对象
	private Command command;
	//受气包,接受命令
	public void setCommand(Command _command){
		this.command = _command;
	}
	//执行命令
	public void action(){
		this.command.execute();
	}
}

优点: 类间解耦,指令类可扩展性高。
缺点: 指令类可能会越来越多。

责任链模式

定义:使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止;其实说到底就是将父处理类保存一个子处理类的节点,如果自己处理不了就调用子节点处理,就类似链表的创建,将判断请求和处理传递的逻辑抽象放在父类里,实现了模板方法模式。

public abstract class Handler {
	private Handler nextHandler;
	//每个处理者都必须对请求做出处理
	public final Response handleMessage(Request request){
		Response response = null;
		//判断是否是自己的处理级别
		if(this.getHandlerLevel().equals(request.getRequestLevel())){
				response = this.echo(request);
			}else{ //不属于自己的处理级别
				//判断是否有下一个处理者
			if(this.nextHandler != null){
				response = this.nextHandler.handleMessage(request);
			}else{
				//没有适当的处理者,业务自行处理
			}
		}
		return response;
	}
	//设置下一个处理者是谁
	public void setNext(Handler _handler){
		this.nextHandler = _handler;
	}
	//每个处理者都有一个处理级别
	protected abstract Level getHandlerLevel();
	//每个处理者都必须实现处理任务
	protected abstract Response echo(Request request);
}

优点: 将请求和处理分开,将请求和处理分开。
缺点: 每个请求都是从链头遍历到链尾,特别是在链比较长的时候,性能是一个非常大的问题,调试不很方便。

装饰模式

定义:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活;原理就是装饰者内部依赖了被装饰者,用户提供被装饰者给装饰类,然后装饰者类将在执行被装饰者的方法上面添加新的逻辑,类似于aop的原理;当然如果想要多层嵌套的装饰,那么需要装饰者类继承被装饰者的抽象类,让装饰者类和被装饰者类一样的传入方式即可。

// 需要被装饰的抽象类
public abstract class Component {
	public abstract void operate();
}

// 具体的被装饰类
public class ConcreteComponent extends Component {
	@Override
	public void operate() {
		System.out.println("do Something");
	}
}

// 抽象的装饰者
public abstract class Decorator extends Component {
	private Component component = null;
	public Decorator(Component _component){
		this.component = _component;
	}
	//委托给被修饰者执行
	@Override
	public void operate() {
		this.component.operate();
	}
}

// 具体的修饰者
public class ConcreteDecorator1 extends Decorator {
	public ConcreteDecorator1(Component _component){
		super(_component);
	}
	//定义自己的修饰方法
	private void method1(){
		System.out.println("method1 修饰");
	}
	//重写父类的Operation方法
	public void operate(){
		this.method1();
		super.operate();
	}
}

优点: 将装饰者和被装饰者解耦。
缺点: 装饰的层数多了可能导致阅读调试困难。

策略模式

定义:定义一组算法,将每个算法都封装起来,并且使它们之间可以互换;原理就是实现接口或者继承类来完成,特别简单。

public interface Strategy {
	//策略模式的运算法则
	public void doSomething();
}

public class ConcreteStrategy1 implements Strategy {
	public void doSomething() {
		System.out.println("具体策略1的运算法则");
	}
}

优点: 算法可以自由切换,避免使用多重条件判断,避免使用多重条件判断。
缺点: 策略类数量增多,所有的策略类都需要对外暴露。

适配器模式

定义:将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作;底层也就是我们创建一个适配器类,该类继承我们需要的抽象类,然后我们在适配器里使用被适配的抽象类,将其处理转换成我们需要的信息返回。

public interface Target {
	//目标角色有自己的方法
	public void request();
}

public class Adaptee {
	//原有的业务逻辑
	public void doSomething(){
		System.out.println("I'm kind of busy,leave me alone,pls!");
	}
}

public class Adapter extends Adaptee implements Target {
	public void request() {
		super.doSomething();
	}
}

优点: 增加了类的透明性,提高了类的复用度,提高了类的复用度。
缺点: 策略类数量增多,所有的策略类都需要对外暴露。

迭代器模式

定义:它提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节;其实也就是java里的迭代器的实现,也就是把遍历容器的行为封装起来,方便使用。
优点: 其实结合了模板方法,方便使用。
缺点: 人为让代码复杂度提高。

组合模式

定义:将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性;简单表示就是定义一个通用节点类,然后具体节点继承,每个具体节点有自己的一个容器,保存着自己的子节点;分为透明模式和安全模式,安全模式就是将不同节点的方法各自实现,而透明模式则是在抽象类里统一实现,然后在实现方法里判断节点类型分别处理。

//  抽象通用节点
public abstract class Component {
	//个体和整体都具有的共享
	public void doSomething(){
		//编写业务逻辑
	}
}

//  具体根和树枝节点
public class Composite extends Component {
	//构件容器
	private ArrayList<Component> componentArrayList = new ArrayList<Component>();
	//增加一个叶子构件或树枝构件
	public void add(Component component){
		this.componentArrayList.add(component);
	}
	//删除一个叶子构件或树枝构件
	public void remove(Component component){
		this.componentArrayList.remove(component);
	}
	//获得分支下的所有叶子构件和树枝构件
	public ArrayList<Component> getChildren(){
		return this.componentArrayList;
	}
}

//  具体叶子节点
public class Leaf extends Component {
	//可以覆写父类方法
	public void doSomething(){
		//  业务逻辑
	}
}

优点: 高层模块调用简单,高层模块调用简单。
缺点: 与依赖倒置原则冲突,限制了接口的影响范围。

观察者模式

定义:也叫做发布订阅模式,定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新;其实就是让被观察者执行动作的时候,主动汇报状态信息给观察者类,java中提供了Obserable类来使用,但是该类被弃用了,可以使用新的Flow流式API来替代,或者使用Rxjava。

// 抽象被观察者类
public abstract class Subject {
	//定义一个观察者数组
	private Vector<Observer> obsVector = new Vector<Observer>();
	//增加一个观察者
	public void addObserver(Observer o){
		this.obsVector.add(o);
	}
	//删除一个观察者
	public void delObserver(Observer o){
		this.obsVector.remove(o);
	}
	//通知所有观察者
	public void notifyObservers(){
		for(Observer o:this.obsVector){
			o.update();
		}
	}
}

public class ConcreteSubject extends Subject {
	//具体的业务
	public void doSomething(){
		/*
		* do something
		*/
		super.notifyObservers();
	}
}

//  抽象观察者接口
public interface Observer {
	//更新方法
	public void update();
}

优点: 观察者和被观察者之间是抽象耦合。
缺点: 增加了运行效率和调试效率问题。

门面模式

定义:也叫外观模式,要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用,门面模式是一个稳定的类,所以不应该参与业务逻辑的运算。

public class ClassA {
	public void doSomethingA(){
		//业务逻辑
	}
}
public class ClassB {
	public void doSomethingB(){
		//业务逻辑
	}
}
public class ClassC {
	public void doSomethingC(){
		//业务逻辑
	}
}

public class Facade {
	//被委托的对象
	private ClassA a = new ClassA();
	private ClassB b = new ClassB();
	private ClassC c = new ClassC();
	//提供给外部访问的方法
	public void methodA(){
		this.a.doSomethingA();
	}
	public void methodB(){
		this.b.doSomethingB();
	}
	public void methodC(){
		this.c.doSomethingC();
	}
}

优点: 减少系统的相互依赖,提高了灵活性和安全性。
缺点: 严重违反了开闭原则。

备忘录模式

定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态;通俗的讲,将数据类写一个创建备份的方法和加载备份的方法,然后创建备份类和备份管理类,通过备份管理类去创建备份,获取备份。

public class Originator {
	//内部状态
	private String state = "";
	public String getState() {
		return state;
	}
	public void setState(String state) {
		this.state = state;
	}
	//创建一个备忘录
	public Memento createMemento(){
		return new Memento(this.state);
	}
	//恢复一个备忘录
	public void restoreMemento(Memento _memento){
		this.setState(_memento.getState());
	}
}

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;
	}
}

public class Caretaker {
	//备忘录对象
	private Memento memento;
	public Memento getMemento() {
		return memento;
	}
	public void setMemento(Memento memento) {
		this.memento = memento;
	}
}

知识点: 当我们想让某个类只能让某个特殊的类访问,我们可以新建一个空接口,然后让该类成为特殊类的成员内部类,该类实现空接口,然后其他类就可以使用该接口来关联该类。
优点: 提供数据的一种备份措施。
缺点: 如果数据量过大,没有及时删除会导致内存资源消耗过大。

访问者模式

定义:封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。

public abstract class Element {
	//定义业务逻辑
	public abstract void doSomething();
	//允许谁来访问
	public abstract void accept(IVisitor visitor);
}

public class ConcreteElement1 extends Element{
	//完善业务逻辑
	public void doSomething(){
		//业务处理
	}
	//允许那个访问者访问
	public void accept(IVisitor visitor){
		visitor.visit(this);
	}
}

public interface IVisitor {
	//可以访问哪些对象
	public void visit(ConcreteElement1 el1);
	public void visit(ConcreteElement2 el2);
}

public class Visitor implements IVisitor {
	//访问el1元素
	public void visit(ConcreteElement1 el1) {
		el1.doSomething();
	}
	//访问el2元素
	public void visit(ConcreteElement2 el2) {
		el2.doSomething();
	}
}

优点: 单一职责,访问者扩展方便,处理数据访问非常灵活。
缺点: 访问者一定要能访问其他类的内部细节,具体被访问者的修改增加比较困难,而且违反了依赖倒转原则。

状态模式

定义:当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类;简单的说就是某个对象有多个状态,状态的改变需要有前置条件的判断,当对象的状态改变执行不同的方法。

public abstract class State {
	//定义一个环境角色,提供子类访问
	protected Context context;
	//设置环境角色
	public void setContext(Context _context){
		this.context = _context;
	}
	//行为1
	public abstract void handle1();
	//行为2
	public abstract void handle2();
}

public class ConcreteState1 extends State {
	@Override
	public void handle1() {
		//本状态下必须处理的逻辑
	}
	@Override
	public void handle2() {
		//设置当前状态为stat2
		super.context.setCurrentState(Context.STATE2);
		//过渡到state2状态,由Context实现
		super.context.handle2();
	}
}

public class Context {
	//定义状态
	public final static State STATE1 = new ConcreteState1();
	public final static State STATE2 = new ConcreteState2();
	//当前状态
	private State CurrentState;
	//获得当前状态
	public State getCurrentState() {
		return CurrentState;
	}
	//设置当前状态
	public void setCurrentState(State currentState) {
		this.CurrentState = currentState;
		//切换状态
		this.CurrentState.setContext(this);
	}
	//行为委托
	public void handle1(){
		this.CurrentState.handle1();
	}
	public void handle2(){
		this.CurrentState.handle2();
	}
}

优点: 结构清晰,遵循设计原则,封装性好。
缺点: 如果状态太多就会导致子类膨胀。

解释器模式

定义:给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子,典型的四则运算表达式就是一种,我们可以将值和符号进行抽象,然后分成不同的子类去处理相应得运算规则,通过递归得方式去调用每个运算规则类,由最小的语法单元解析完成。

public abstract class Expression {
	//每个表达式必须有一个解析任务
	public abstract Object interpreter(Context ctx);
}

public class TerminalExpression extends Expression {
	//通常终结符表达式只有一个,但是有多个对象
	public Object interpreter(Context ctx) {
		return null;
	}
}

public class NonterminalExpression extends Expression {
	//每个非终结符表达式都会对其他表达式产生依赖
	public NonterminalExpression(Expression... expression){
	}
	public Object interpreter(Context ctx) {
		//进行文法处理
		return null;
	}
}

public class Client {
	public static void main(String[] args) {
		Context ctx = new Context();
		//通常定一个语法容器,容纳一个具体的表达式,通常为ListArray、LinkedList、Stack等类型
		Stack&Expression> stack = null;
		for(;;){
		//进行语法判断,并产生递归调用
		}
		//产生一个完整的语法树,由各个具体的语法分析进行解析
		Expression exp = stack.pop();
		//具体元素进入场景
		exp.interpreter(ctx);
	}
}

优点: 扩展性很强,想要增加语法规则直接增加类即可。
缺点: 类得膨胀总是随着扩展性出现;递归运算总是伴随着调试困难而出现,所以可能导致效率会偏低。
使用场景: 适合大量重复发生得问题,:例如对不同的日志系统解析;语法或者规则固定的场景,例如sql语句的语法。

享元模式

定义:使用共享对象可有效地支持大量的细粒度的对象,将对象的某些相同属性提取出来作为外部枚举或者固定对象,通过标识去获取这些对象。

public abstract class Flyweight {
//内部状态
private String intrinsic;
	//外部状态
	protected final String Extrinsic;
	//要求享元角色必须接受外部状态
	public Flyweight(String _Extrinsic){
		this.Extrinsic = _Extrinsic;
	}
	//定义业务操作
	public abstract void operate();
		//内部状态的getter/setter
		public String getIntrinsic() {
		return intrinsic;
	}
	public void setIntrinsic(String intrinsic) {
		this.intrinsic = intrinsic;
	}
}

public class ConcreteFlyweight1 extends Flyweight{
	//接受外部状态
	public ConcreteFlyweight1(String _Extrinsic){
		super(_Extrinsic);
	}
	//根据外部状态进行逻辑处理
	public void operate(){
		//业务逻辑
	}
}

public class FlyweightFactory {
	//定义一个池容器
	private static HashMap<String,Flyweight> pool= new HashMap<String,Flyweight>();
	//享元工厂
	public static Flyweight getFlyweight(String Extrinsic){
		//需要返回的对象
		Flyweight flyweight = null;
		//在池中没有该对象
		if(pool.containsKey(Extrinsic)){
			flyweight = pool.get(Extrinsic);
		}else{
			//根据外部状态创建享元对象
			flyweight = new ConcreteFlyweight1(Extrinsic);
			//放置到池中
			pool.put(Extrinsic, flyweight);
		}
		return flyweight;
	}
}

优点: 减少内存的占用量,增加程序性能。
缺点: 增加了系统的复杂性,并且会导致多线程下的并发问题。
使用场景: 当系统需要减少对象大量创建的需求;需要缓冲池的需求。
注意: 当我们使用map容器对象时,最好使用java基本类型和string类型,能极大提高运行效率。

桥梁模式

定义:将抽象和实现解耦,使得两者可以独立地变化,从实现来看有点像抽象工厂模式,但是桥梁分离的是业务实际实现和抽象的基本对象。

public interface Implementor {
	//基本方法
	public void doSomething();
	public void doAnything();
}

public class ConcreteImplementor1 implements Implementor{
	public void doSomething(){
		//业务逻辑处理
	}
	public void doAnything(){
		//业务逻辑处理
	}
}

public abstract class Abstraction {
	//定义对实现化角色的引用
	private Implementor imp;
	//约束子类必须实现该构造函数
	public Abstraction(Implementor _imp){
		this.imp = _imp;
	}
	//自身的行为和属性
	public void request(){
		this.imp.doSomething();
	}
	//获得实现化角色
	public Implementor getImp(){
		return imp;
	}
}

public class RefinedAbstraction extends Abstraction {
	//覆写构造函数
	public RefinedAbstraction(Implementor _imp){
		super(_imp);
	}
	//修正父类的行为
	@Override
	public void request(){
		/*
		* 业务处理...
		*/
		super.request();
		super.getImp().doAnything();
	}
}

优点: 扩展性强,封装性强,结构清晰。
缺点: 类的膨胀,系统复杂性提高。
使用场景: 类和接口都不稳定的情况;继承机制无法实现的情况;设计的颗粒度需要更细的情况。

设计模式对比

工厂模式对比建造者模式

相同点: 目的都是创建对象返回。
不同点: 关注点不同,工厂模式创建的对象是整体的产品,而建造者模式关注点是产品创建的过程,也就是将产品的创建步骤由导演类定义,使用建造者类创建,哪怕每个组件属性都一样,顺序不同导致返回的结果也会不同。

抽象工厂模式对比建造者模式

相同点: 也是都创建对象,不过抽象工厂将产品和品牌抽象分开了。
不同点: 和工厂模式对比建造者模式一样。

代理模式对比装饰模式

相同点: 同样的使用了某个对象,给对象的某个方法增加额外的逻辑和功能。
不同点: 代码写法都一样,但是代理模式对被代理对象有着绝对的控制权,也就是能够决定被代理对象的方式是否执行以及传递参数的处理;但是装饰模式只是用于被装饰对象的方法额外执行逻辑,不能改变原对象的方法执行流程和参数,而且装饰类可以通过抽象的方式层层包装被装饰对象。

装饰模式对比适配器模式

相同点: 都是通过继承或者实现接口的方式去修改构造时传入对象的行为。
不同点: 装饰模式通过继承和被装饰对象的同样抽象类,去覆写实现方法,通过调用装饰类的方法增强方法或者减弱方法;适配器模式则是通过适配器类继承目标对象父类,接收源对象,然后将源对象封装伪造成目标对象。

命令模式对比策略模式

相同点: 代码结构相似。
不同点: 命令模式关注点是将命令和执行命令的接受者解耦,通过不同的命令去调用相应接收者的方法,甚至可以实现撤销的操作;策略模式关注点就是将某种固定算法,或者其他运算模式抽象出来,能够相互替换使用即可,较为简单。

策略模式对比状态模式

相同点: 代码结构相似。
不同点: 策略模式是对算法的封装抽象;而状态模式则是通过抽象出不同的状态,改变对象不同的行为,甚至状态的改变之间有着严格的规范和限制,而且会让对象转变成另一种状态。

观察者模式对比责任链模式

相同点: 观察者模式可以通过事件调用的方式实现链式调用,而责任链模式也是链式调用。
不同点: 观察者模式里,被观察者类通过事件调用观察者类的方法,而观察者又可以作为被观察者再调用其他事件观察者,对于参数的传递可以改变,只要两者相互知道就可以,而且也可以不用链式调用,也可以广播;而责任链模式就是必须链式调用,而且参数不会被修改,只能传递。

策略模式对比桥梁模式

相同点: 两者结构特别相似。
不同点: 桥梁模式对比策略模式,就是将实现的类别和使用实现类的调用者分别抽象出来,可以各自变化;而策略只是封装了算法,让算法自由变化。

门面模式对比中介者模式

相同点: 都是通过一个统一入口去访问实现方法。
不同点: 门面模式是通过一个入口类去访问所有内部的子系统业务,就想是统一门户系统一样;中介者模式目的是让各个类或者业务之间耦合分离,通过中介者类实现具体业务逻辑,然后每个业务之间去调用中介者类,达到解耦的效果。

设计模式混合使用

命令模式+责任链模式

我们在linux系统使用的命令,在windows系统里是绝对使用不了的,我们可以写一个程序实现在windows系统使用linux命令。通过命令模式将不同的命令分给不同的命令解析类,而命令解析类又是通过责任链模式建立,这样我们将命令参数下发解析,就可以对命令里不同的参数值做出不同的解析结果。

工厂方法模式+策略模式

对于买东西消费扣款的策略,我们可以使用策略模式实现,很简单,但是有个问题就是策略实现类必须暴露给上层模块,所以我们使用工厂模式加上配置的模式,去创建具体消费策略,这样我们就可以实现不同消费活动采用不同的扣款策略。

观察者模式+中介者模式

通过观察者模式,我们可以得到一个时间触发的回调机制,其实就是相当于我们网页上面的点击事件,加载事件等等,某些行为触发了某些事件,然后通知观察者去执行我们的处理方法,但是处理方法上,事件其实一般都会有相互的业务操作,我们就需要一个中介者将内部的业务封装了,然后将每个事件处理类解耦,到时候观察者直接调用中介者类即可。

总结

其实对于设计模式,不是说开发中一定要使用某个模式才行,这些模式只是辅助我们设计一个好的系统,帮助我们对于某些设计不当的系统改良,每个设计模式都有优点和缺点,我们需要按需使用,才能设计出一个有实际意义的项目。

扩展

MVC框架

也就是我们目前流行的模型-视图-控制器框架,该框架的目的主要就是将试图和模型数据解耦,之前我们都是使用jsp模式,将模型和视图写在一起了,现在解耦了后,我们可以让视图只作为显示,模型只作为数据获取,而控制器则是将数据组装发送给视图,这样我们就可以让同样的数据在不同的平台或者终端显示,也能让不同的数据显示在同样的页面上。
mvc的框架大体上就是通过请求分发器去接受请求,找到映射的处理器,然后调用过滤器,执行拦截器链,最后发送到处理器去执行相应逻辑。

新模式

雇工模式,空对象模式,黑板模式,规格模式等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值