原型模式
实现Cloneable接口并重写clone()方法,就完成了原型模式。
通用源码:
public class ProtoTypeClass implements Cloneable{
@Override
public ProtoTypeClass clone(){
ProtoTypeClass protoType = null;
try{
protoType = (ProtoTypeClass)super.clone();
}catch(CloneNotSupportedException e){
//异常处理
}
return protoType;
}
}
- 优点:原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多。尤其是要在一个循环体内产生大量的对象时。
- 缺点:逃避构造函数的约束。直接在内存中拷贝,构造函数是不会执行的。Object类的clone方法的原理就是从内存中以二进制流的方式进行拷贝,重新分配一个内存块。
- 应用场景:
- 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
- 通过new产生一个对象需要非常多繁琐的数据准备货访问权限。
- 一个对象多个修改者。有可能产生数据不一致的问题。可以考虑使用原型模式拷贝多个对象供调用者使用。
实际项目中原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。
浅拷贝和深拷贝
浅拷贝:Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝。还是指向原生对象的内部地址。而其他的原始类型比如int,long,char等都会被拷贝,但对于String类型,java希望你把它认为是基本的原始类型,它是没有clone()方法的。处理机制也比较特殊,通过字符串池(stringpool)在需要的时候才在内存中创建新的字符串。
注:使用原型模式时,引用的成员变量必须满足两个条件才不会被拷贝:一是类的成员变量,而不是方法内变量;而是必须是一个可变的引用对象,而不是原始类型或不可变对象。
public class Thing implements Cloneable{
private ArrayList<String> arrayList = new ArrayList<String>();
@Override
public Thing clone(){
Thing thing=null;
try {
thing = (Thing)super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return thing;
}
public void setValue(String value){
this.arrayList.add(value);
}
public ArrayList<String> getValue(){
return this.arrayList;
}
}
public static void main(String[] args) {
Thing thing = new Thing();
thing.setValue("李四");
Thing cloneThing = thing.clone();
cloneThing.setValue("张三");
System.out.println(thing.getValue());//结果是[李四,张三]
}
深拷贝:改下上面clone()方法的代码即可实现深拷贝。
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;
}
注:要实现深拷贝,类的成员变量不要增加final关键字,否则会报错。
最佳实践:原型模式先产生出一个包含大量共有信息的类,然后可以拷贝出副本,修正细节信息,建立一个完整的个性对象。
中介者模式
迪米特法则认为每个类只和朋友类交流。
中介者模式定义:用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示的相互作用,使其耦合松散,而且可以独立的改变他们之间的交互。
中介者模式通用类图:
ConcreteMediator—>Mediator<——-Colleague
Mediator:抽象中介者,定义统一的接口,用于各同事角色之间的通信。
Concrete Mediator:具体中介者,协调各同事角色实现协作行为,因此它必须依赖于各个同事角色。
Colleague:每一个同事角色都知道中介者角色,每一个同事类的行为分为两种:一种是同事本身的行为,比如改变对象本身的状态,处理自己的行为等。一种是必须依赖中介者才能完成的行为,叫做依赖方法。
通用代码:
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 class ConcreteMediator extends Mediator {
@Override
public void doSomething1() {
//调用同事类的方法
super.c1.selfMethod1();
super.c2.selfMethod2();
}
public void doSomething2() {
super.c1.selfMethod1();
super.c2.selfMethod2();
}
}
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);
}
//自有方法
public void selfMethod1(){
//处理自己的业务逻辑
}
//依赖方法 dep-method
public void depMethod1(){
//处理自己的业务逻辑
//自己不能处理的业务逻辑,委托给中介者处理
super.mediator.doSomething1();
}
}
public class ConcreteColleague2 extends Colleague {
public ConcreteColleague2(Mediator _mediator){
super(_mediator);
}
public void selfMethod2(){
//处理自己的业务逻辑
}
public void depMethod2(){
//处理自己的业务逻辑
super.mediator.doSomething2();
}
}
- 优点:减少类间的依赖,把原有的一对多的依赖变成了一对一的依赖,同事类只依赖中介者。降低了类间的耦合。
- 缺点:中介者会膨胀的很大,同事类越多,中介者的逻辑就越复杂。
- 应用:中介者模式的使用要量力而行。适用于多个对象之间的紧密耦合的情况。紧密耦合的标准是:在类图中出现了蜘蛛网结构。把蜘蛛网梳理为星型结构。
- 实际应用:
机场调度中心。
MVC框架:struts的C(Controller)就是一个中介者。把Model和Vie
w隔离开,协调M和V协同工作,减少他们之间的依赖。
媒体网关、中介服务等 - 最佳实践:
- N个对象之间产生了相互的依赖关系(N>2)
- 多个对象有依赖关系,但是依赖的行为尚不确定或者有发生改变的可能。可使用中介者模式降低变更引起的风险扩散。
- 产品开发。把中介者模式应用到产品中,可以提升产品的性能和扩展性。
命令模式
定义:将一个请求封装成一个对象,从而让你使用不同的请求吧客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
命令模式的通用类图:
Receiver:接受者角色。干活的角色。命令传到这里是应该被执行的。
Command命令角色:需要执行的所有命令都在这里执行。
Invoker调用者角色:接收到命令并执行命令。
通用代码
public abstract class Receiver{
//抽象接受者,定义每个接受者都必须完成的业务
public abstract void doSomething();
}
public class ConcreteReciver1 extends Receiver{
//每个接收者必须处理一定的业务逻辑
public void doSomething(){
}
}
public class ConcreteReciver2 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 ConcreteCommand2 extends Command {
private Receiver receiver;
public ConcreteCommand2(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 class Client {
public static void main(String[] args) {
Invoker invoker = new Invoker();
Receiver receiver = new ConcreteReciver1();
Command command = new ConcreteCommand1(receiver);
invoker.setCommand(command);
invoker.action();
}
}
优点:
1. 类间解耦,调用者角色与接收者角色之间没有任何依赖关系。调用者实现功能时只需调用Command抽象类的execute方法就可以了。
2. 可扩展性:Command的子类非常容易扩展。
3. 命令模式可以结合责任链模式,实现命令族解析,结合模板方法模式,则可以减少Command子类的膨胀问题。
缺点:Command的子类会膨胀。