??xml version="1.0" encoding="utf-8" standalone="yes"?>
public interface Person {
void accept(Visitor visitor);
}
public class Woman implements Person{
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
public class Man implements Person{
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
讉K者接口与实现c,分别代表男h与女人在不同的状态下的表?br />
public interface Visitor {
public void visit(Man man);
public void visit(Woman girl);
}
//成功时Man与Woman的不同表?br />
public class Success implements Visitor{
public void visit(Man man) {
System.out.println("当男人成功时Q背后多半有一个伟大的女h");
}
public void visit(Woman woman) {
System.out.println("当女人成功时Q背后大多有一个不成功的男?);
}
}
//恋爱时Man与Woman的不同表?br />
public class Love implements Visitor{
public void visit(Man man) {
System.out.println("当男人恋爱时Q凡事不懂也装懂");
}
public void visit(Woman girl) {
System.out.println("当女人恋爱时Q遇事懂也装不懂");
}
}
ObjectStructure与客L(fng)试代码
import java.util.*;
public class ObjectStructure {
private List<Person> elements = new ArrayList<Person>();
public void attach(Person element){
elements.add(element);
}
public void detach(Person element){
elements.remove(elements);
}
//遍历各种具体元素q执行他们的acceptҎ(gu)
public void display(Visitor visitor){
for(Person p:elements){
p.accept(visitor);
}
}
}
public class Client {
public static void main(String[] args) {
ObjectStructure o = new ObjectStructure(); //依赖于ObjectStructure
//实例化具体元?br />
o.attach(new Man());
o.attach(new Woman());
//当成功时不同元素的不同反?br />
Visitor success = new Success(); //依赖于抽象的Visitor接口
o.display(success);
//当恋爱时的不同反?br />
Visitor amativeness = new Love(); //依赖于抽象的Visitor接口
o.display(amativeness);
}
}
需求的变化
假设现在需求要扩展数据l构Q增加一U具体元素,男与女之外的一U不明物体,我们暂时把它UCؓ(f)“怪兽”Q在既有讉K者模式的架构下,应该怎样Q首先增加一个Brucec,实现Person接口。最ȝ的是要修改访问者接口及(qing)其所有具体访问?
因ؓ(f)VisitҎ(gu)中没有包含访问Bruce对象的行为,因此我们被迫要去手工更改Visitor(包括抽象的,具体?Q在其中d有关Bruce对象的行为,q严重违反了“开?闭”原则。究其原因在于目前的l构下,被访问对象与讉K对象互相依赖Q自然不利于分离变化Q必d掉一层依赖关pR?br />
我们试把Visitor对Person(元素)的依赖关pd掉,抽象出对应每个具体元素的ElementVisitor接口 -->ManVisitor,WomanVisitorQ然后把Visitor对Person的依赖关p{UdManVisitor?WomanVisitorw上?br />
现在Visitor接口已经没有M抽象Ҏ(gu)Q只是一个空接口Q每一个具体元素对应有一个ElementVisitor接口Q每一个元素对应的ElementVisitor接口有访问该元素的visit(),相当把原来在Visitor接口中声明工作,交由各个具体ElementVisitor接口完成?br />
l过攚w后的代码:(x)
原Visitor接口
public interface Visitor {
//退化到没有M抽象Ҏ(gu)
}
新增加ManVisitor,WomanVisitor接口
public interface ManVisitor {
public void visit(Man man);
}
public interface WomanVisitor {
public void visit(Woman w);
}
具体Visitor实现cȝ在同时实?个接?br />
//由实现Visitor接口扩展成实现Visitor,WomanVisitor,ManVisitor三个接口
public class Success implements Visitor,WomanVisitor,ManVisitor{
public void visit(Man man) {
System.out.println("当男人成功时Q背后多半有一个伟大的女h");
}
public void visit(Woman girl) {
System.out.println("当女人成功时Q背后大多有一个不成功的男?);
}
}
//由实现Visitor接口扩展成实现Visitor,WomanVisitor,ManVisitor三个接口
public class Love implements Visitor,WomanVisitor,ManVisitor{
public void visit(Man man) {
System.out.println("当男人恋爱时Q凡事不懂也装懂");
}
public void visit(Woman girl) {
System.out.println("当女人恋爱时Q遇事懂也装不懂");
}
}
Person接口没有变化,依旧只依赖于Visitor接口
public interface Person {
void accept(Visitor visitor);
}
攚w后的具体元素类Man与Woman
public class Man implements Person {
// 先对visitorq行cd转换Q再执行visitҎ(gu)Q因为Visitor接口已经没有声明M抽象Ҏ(gu)?br />
public void accept(Visitor visitor) {
if (visitor instanceof ManVisitor) {
ManVisitor mv = (ManVisitor) visitor;
mv.visit(this);
}
}
}
public class Woman implements Person {
// 先对visitorq行cd转换Q再执行visitҎ(gu)Q因为Visitor接口已经没有声明M抽象Ҏ(gu)?br />
public void accept(Visitor visitor) {
if (visitor instanceof WomanVisitor) {
WomanVisitor wv = (WomanVisitor) visitor;
wv.visit(this);
}
}
}
ObjectStructure与客L(fng)试代码没有变化
import java.util.*;
public class ObjectStructure {
private List<Person> elements = new ArrayList<Person>();
public void attach(Person element){
elements.add(element);
}
public void detach(Person element){
elements.remove(elements);
}
//遍历各种具体元素q执行他们的acceptҎ(gu)
public void display(Visitor visitor){
for(Person p:elements){
p.accept(visitor);
}
}
}
public class Client {
public static void main(String[] args) {
ObjectStructure o = new ObjectStructure(); //依赖于ObjectStructure
//实例化具体元?br />
o.attach(new Man());
o.attach(new Woman());
//当成功时不同元素的不同反?br />
Visitor success = new Success(); //依赖于抽象的Visitor接口
o.display(success);
//当恋爱时的不同反?br />
Visitor amativeness = new Love(); //依赖于抽象的Visitor接口
o.display(amativeness);
}
}
x攚w完毕!我们执行客户端测试代码,l果昄Q?br />
当男人成功时Q背后多半有一个伟大的女h
当女人成功时Q背后大多有一个不成功的男?br />
当男人恋爱时Q凡事不懂也装懂
当女人恋爱时Q遇事懂也装不懂
此时Q客L(fng)仍然只依赖于VisitorI接口与ObjectStructurecR可能一开始大家会(x)认ؓ(f)I接口没有什么用Q现在就能体现出他的威力了,使客L(fng)与具体Visitor的高度解耦!也正是这U思维的核心在JavaAPI中也有类似的应用Q这U空接口被称为标识接口。比如java.io.Serializable与java.rmi.Remote{,标识接口里没有Q何方法和属性,标识不对实现接口不对实现它的cLM语义上的要求Q它仅仅是表明实现它的类属于一U特定的cd?br />
上面具体讉K者实现的多个接口被称为合类型。这个概c(din)Java与模式》中有提?qing)过Q当一个具体类处于一个类的等U结构之中的时候,个具体类定义一个合类型是可以保证Zq个cd的可插入性的关键?br />
=================================无敌分界U?===================================
讲了q么长,现在我们试下改造后的访问者模?br />
首先增加一U行?状?Q即原访问者模式的优点
增加一个具体访问者Fail,修改一下客L(fng)试代码
public class Fail implements Visitor,ManVisitor,WomanVisitor{
public void visit(Man man) {
System.out.println("当男人失败时Q闷头喝酒,谁也不用?);
}
public void visit(Woman woman) {
System.out.println("当女人失败时Q眼泪汪汪,谁也劝不?);
}
}
public class Client {
public static void main(String[] args) {
ObjectStructure o = new ObjectStructure(); //依赖于ObjectStructure
//实例化具体元?br />
o.attach(new Man());
o.attach(new Woman());
//当成功时不同元素的不同反?br />
Visitor success = new Success(); //依赖于抽象的Visitor接口
o.display(success);
System.out.println();
//当恋爱时的不同反?br />
Visitor amativeness = new Love(); //依赖于抽象的Visitor接口
o.display(amativeness);
System.out.println();
//新增加失败时的不同反?br />
Visitor fail = new Fail();
o.display(fail);
}
}
l果昄Q?br />
当男人成功时Q背后多半有一个伟大的女h
当女人成功时Q背后大多有一个不成功的男?br />
当男人恋爱时Q凡事不懂也装懂
当女人恋爱时Q遇事懂也装不懂
当男人失败时Q闷头喝酒,谁也不用?br />
当女人失败时Q眼泪汪汪,谁也劝不?br />
增加新的行ؓ(f)(状?与原来一h便!只需要增加一个具体访问者即可!
现在我们来增加一个具体元?正是写这文章的初衷)
首先增加一个具体元素Bruce
public class Bruce implements Person{
public void accept(Visitor visitor) {
if(visitor instanceof BruceVisitor){
BruceVisitor bv = (BruceVisitor) visitor;
bv.visit(this);
}
//q个else可写可不?br />
else{
String s = visitor.getClass().getName();
String state = s.substring(s.lastIndexOf(".")+1,s.length());
System.out.println("?.原来怪兽?+state+"的时候是没有行ؓ(f)?!");
}
}
}
//按照新的思维方式增加一个对应的ElementVisitor接口
public interface BruceVisitor {
public void visit(Bruce bruce);
}
我们让Successq个具体讉K者多实现一个BruceVisitor讉K者接?和修改一下客L(fng)代码q行试
public class Success implements Visitor,WomanVisitor,ManVisitor,BruceVisitor{
public void visit(Man man) {
System.out.println("当男人成功时Q背后多半有一个伟大的女h");
}
public void visit(Woman girl) {
System.out.println("当女人成功时Q背后大多有一个不成功的男?);
}
public void visit(Bruce bruce) {
System.out.println("当怪兽成功?........无语..........");
}
}
public class Client {
public static void main(String[] args) {
ObjectStructure o = new ObjectStructure(); //依赖于ObjectStructure
o.attach(new Man());
o.attach(new Woman());
o.attach(new Bruce()); //新增一U具体元素Bruce
Visitor success = new Success(); //依赖于抽象的Visitor接口
o.display(success);
System.out.println();
Visitor amativeness = new Love(); //依赖于抽象的Visitor接口
o.display(amativeness);
System.out.println();
Visitor fail = new Fail();
o.display(fail);
}
}
昄l果Q?br />
当男人成功时Q背后多半有一个伟大的女h
当女人成功时Q背后大多有一个不成功的男?br />
当怪兽成功?........无语..........
当男人恋爱时Q凡事不懂也装懂
当女人恋爱时Q遇事懂也装不懂
?.原来怪兽在Love的时候是没有行ؓ(f)?!
当男人失败时Q闷头喝酒,谁也不用?br />
当女人失败时Q眼泪汪汪,谁也劝不?br />
?.原来怪兽在Fail的时候是没有行ؓ(f)?!
q个l果你满意吗Q?br />
虽然Q这只是部分W合“开?闭”原则Q我们不需要修改Visitor接口Q但q是得去修改Success实现新的接口。但是修改具体类比修Ҏ(gu)口的代h(hun)得多,不需要重新编译所有访问接口和具体讉K者。我们面对新的变化也容易得多。而且q还有一个好处,是可以让各U元素有选择地让别h讉KQ如上述例子Q这样讉K者模式的q用h更加灉|?
客户的开发h员定义了一个接口,期望用这个接口来完成整数的求和操作,接口定义如下Q?
public interface Operation{
public int add(int a,int b);
}
开发h员在了解q个接口的定义后Q发C个第三方c,里面有一个方法能实现他们期望的功能,其代码如下:(x)
public class OtherOperation{
public int otherAdd(int a,int b){
return a + b;
}
}
以上W三方类OtherOperation的方法public int otherAdd(int a,int b)所提供的功能,完全能符合客L(fng)期望Q所以只需要想办法把OtherOperation的otherAdd(int a,int b)和客L(fng)Operation接口联系hQ让q个W三方类来ؓ(f)客户提供他们期望的服务就行了Q这样就避免了开发h员再度去研究cMOtherOperation的otherAdd(int a,int b)Ҏ(gu)的实玎ͼ利用已有的轮子,避免重复发明Q,q方法之一Q就是用适配器模式:(x)
public class AdapterOperation extends OtherOperation implements Operation{
public int add(int a,int b){
return otherAdd(a,b);
}
}
以上是适配器的实现Ҏ(gu)之一Q类适配器,在以上实C存在着三中角色分别是:(x)
1Q适配目标角色QOperation?
2Q适配c(原)角色QOtherOperation?
3Q适配器角Ԍ(x)AdapterOperation?
其中适配器角色是适配器模式的核心?
适配器的主要工作是通过装现有的功能,使他满需要的接口?
对象适配?
我们再来看看另一U情况:(x)
假如客户接口期望的功能不止一个,而是多个Q?
public interface Operation{
public int add(int a,int b);
public int minus(int a,int b);
public int multiplied(int a,int b);
}
而能提供q些实现的原可能不止一个:(x)
public class OtherAdd{
public int otherAdd(int a,int b){
return a + b;
}
}
public class OtherMinus{
public int minus(int a,int b){
return a - b;
}
}
public class OtherMultiplied{
public int multiplied(int a,int b){
return a * b;
}
}
׃java是不能实现多l承的,所以我们不能通过构徏一个适配器,让他来承所有原以完成我们的期望Q这时候怎么办呢?只能用适配器的另一U实?-对象适配器:(x)
public class AdapterOperation implements Operation{
private OtherAdd add;
private OtherMinus minus;
private OtherMultiplied multiplied;
public void setAdd(OtherAdd add){
this.add = add;
}
public void setMinus(OtherMinus minus){
this.minus = minus;
}
public void setMultiplied(OtherMultiplied multiplied){
this.multiplied = multiplied;
}
//适配加法q算
public int add(int a,int b){
return add.otherAdd(a,b);
}
//适配减法q算
public int minus(int a,int b){
return minus.minus(a,b);
}
//适配乘法q算
public int multiplied(int a,int b){
return multiplied.multiplied(a,b);
}
}
上面代码很明显,适配器ƈ不是通过l承来获取适配c(原)的功能的Q而是通过适配cȝ对象来获取的Q这p决了java不能多承所带来的不便了。这也是java提倡的~程思想之一Q即量使用聚合不要使用l承?q有一U情冉|需要用对象适配器的。我们来看看Q单我们的客h供的需求ƈ不是一个明的接口Q而是一个类Qƈ没有定义期望的方法,如下
public class A{
public int add(int a,int b){
return a + b;
}
}
现在客户要一个新cBQ要求能在保留类A功能的情况下增加一个运减法的功能Qƈ要求B能随时替换掉A但不能对已有pȝ造成影响。这h们只能新Z个类BQƈ让Bl承A?
public class B extends A{
b(){
super();
}
public int minus(int a,int b){
//待实现的减法q算函数..
}
}
q时候,我们发现cC已经提供了实现减法的函数Q?
public class C{
public int minus(int a,int b){
return a - b;
}
}
Z避免重复去设计该函数Q我们决定引入Cc,通过适配CcL辑ֈ我们的期望,但问题是A和C都是一个具体类Q我们无法让B同时l承q个两个c,而Bl承A又是必须的,所以我们只能考虑把Cl内聚到B内部Q对象适配器又得派上用Z?
public class B extends A{
private C c;
B(){
super();
}
public void setMinus(C c){
this.c= c;
}
public int minus(int a,int b){
return c.minus(a,b);
}
}
q样Q在需要Acȝ地方都能用BcL代替Q同时又保证了新的功能的引入?
更灵zȝ实现--隐藏目标接口的抽象适配?
做java 桌面应用的都知道WindowListener接口Q?
public interface WindowListener extends EventListener{
public void windowActivated(WindowEvent e)Q?br />
public void windowClosed(WindowEvent e)Q?br />
public void windowClosing(WindowEvent e)Q?br />
public void windowDeactivated(WindowEvent e)Q?br />
public void windowDeiconified(WindowEvent e)Q?br />
public void windowIconified(WindowEvent e)Q?br />
public void windowOpened(WindowEvent e)Q?br />
}
要实现这个接口,我们必d现它所定义的所有方法,但是实际上,我们很少需要同时用到所有的Ҏ(gu)Q我们要的只是其中的两三个。ؓ(f)了不使我们实现多余的Ҏ(gu)Q?
jdk WindowListener提供了一个WindowListener的默认实现类WindowAdapterc,q是一个抽象类Q?
public abstract class WindowAdapter implements WindowListener{
public void windowActivated(WindowEvent e){}
public void windowClosed(WindowEvent e){}
public void windowClosing(WindowEvent e){}
public void windowDeactivated(WindowEvent e){}
public void windowDeiconified(WindowEvent e){}
public void windowIconified(WindowEvent e){}
public void windowOpened(WindowEvent e){}
}
WindowAdaptercdWindowListener接口的所有有Ҏ(gu)都提供了I实玎ͼ
有了WindowAdapterc,我们只需要去l承WindowAdapterQ然后选择我们所兛_的方法来实现p了,q样避免了直接d现WindowListener接口?
class WindowsSystem{
private String state;
public Memento createMemento(){ //创徏pȝ备䆾
return new Memento(state);
}
public void restoreMemento(Memento m){ //恢复pȝ
this.state=m.getState();
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
System.out.println("当前pȝ处于"+this.state);
}
}
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;
}
}
class User{
private Memento memento;
public Memento retrieveMemento() { //恢复pȝ
return this.memento;
}
public void saveMemento(Memento memento){ //保存pȝ
this.memento = memento;
}
}
public class Test{
public static void main(String[] args) {
WindowsSystem Winxp = new WindowsSystem(); //Winxppȝ
User user = new User(); //某一用户
Winxp.setState("好的状?); //Winxp处于好的q行状?br />
user.saveMemento(Winxp.createMemento()); //用户对系l进行备份,W(xu)inxppȝ要生备份文?br />
Winxp.setState("坏的状?); //Winxp处于不好的运行状?br />
Winxp.restoreMemento(user.retrieveMemento()); //用户发恢复命令,pȝq行恢复
System.out.println("当前pȝ处于"+Winxp.getState());
}
}
在本例中QW(xu)indowsSystem是发起h角色(Orignation),Memento是备忘录角色(Memento),User是备忘录理角色(Caretaker)。Memento提供了两个接口(注意q里的接?q不是java中的接口Q它指的是可被外界调用的Ҏ(gu)Q:(x)一个是为WindowsSystem cȝ宽接口,能够得到WindowsSystem攑օMemento的state属性,代码见WindowsSystem的createMementoҎ(gu)和restoreMementoҎ(gu)QcreateMementoҎ(gu)向Memento攑օstate属性,restoreMementoҎ(gu)获得攑օ的state属性。另一个是为UsercL供的H接口,只能理Memento而不能对它的内容q行M操作Q见Userc)?br />
五?优缺?br />
1Q?保持装边界 使用备忘录可以避免暴露一些只应由原发器管理却又必d储在原发器之外的信息。该模式把可能很复杂的Originator内部信息对其他对象屏蔽v来,从而保持了装边界?
2Q?它简化了原发?在其他的保持装性的设计中,Originator负责保持客户hq的内部状态版本。这把所有存储管理的重Q交给了Originator。让客户理它们h的状态将?x)简化OriginatorQƈ且得客户工作结束时无需通知原发器?
3Q?使用备忘录可能代价很?如果原发器在生成备忘录时必须拯q存储大量的信息Q或者客户非帔RJ地创徏备忘录和恢复原发器状态,可能?x)导致非常大的开销。除非封装和恢复Originator状态的开销不大Q否则该模式可能q不合适?
4Q?l护备忘录的潜在代h(hun) 理器负责删除它所l护的备忘录。然而,理器不知道备忘录中有多个状态。因此当存储备忘录时Q一个本来很的理器,可能?x)生大量的存储开销?br />
六?适用?br />
1Q必M存一个对象在某一个时ȝQ部分)状态,q样以后需要时它才能恢复到先前的状态?br />
2Q如果一个用接口来让其它对象直接得到q些状态,会(x)暴露对象的实现细节ƈ破坏对象的封装性?/font>
同事cLQ?br />
//抽象同事c?nbsp;
abstract class AbstractColleague {
protected AbstractMediator mediator;
/**既然有中介者,那么每个具体同事必然要与中介者有联系Q?br />
* 否则没必要存在?q个pȝ当中Q这里的构造函数相?
* 于向该系l中注册一个中介者,以取得联p?nbsp;
*/
public AbstractColleague(AbstractMediator mediator) {
this.mediator = mediator;
}
// 在抽象同事类中添加用于与中介者取得联p(x册)的方?br />
public void setMediator(AbstractMediator mediator) {
this.mediator = mediator;
}
}
//具体同事A
class ColleagueA extends AbstractColleague {
//每个具体同事都通过父类构造函C中介者取得联p?
public ColleagueA(AbstractMediator mediator) {super(mediator);}
//每个具体同事必然有自己分内的事,没必要与外界相关?
public void self(){
System.out.println("同事A --> 做好自己分内的事?...");
}
//每个具体同事L需要与外界交互的操作,通过中介者来处理q些逻辑q安排工?br />
public void out() {
System.out.println("同事A --> h同事B做好分内工作 ...");
super.mediator.execute("ColleagueB", "self");
}
}
//具体同事B
class ColleagueB extends AbstractColleague {
public ColleagueB(AbstractMediator mediator) {
super(mediator);
}
public void self() {
System.out.println("同事B --> 做好自己分内的事?...");
}
public void out() {
System.out.println("同事B --> h同事A做好分内工作 ...");
super.mediator.execute("ColleagueA", "self");
}
}
//抽象中介?nbsp;
abstract class AbstractMediator {
//中介者肯定需要保持有若干同事的联pL?nbsp;
protected Hashtable<String, AbstractColleague> colleagues = new Hashtable<String, AbstractColleague>();
//中介者可以动态地与某个同事徏立联p?nbsp;
public void addColleague(String name, AbstractColleague c) {
this.colleagues.put(name, c);
}
//中介者也可以动态地撤销与某个同事的联系
public void deleteColleague(String name) {
this.colleagues.remove(name);
}
//中介者必d备在同事之间处理逻辑、分配Q务、促q交的操作
public abstract void execute(String name, String method);
}
//具体中介?
class Mediator extends AbstractMediator{
//中介者最重要的功能,来回奔L与各个同事之?
public void execute(String name, String method) {
if("self".equals(method)){
//各自做好分内?
if("ColleagueA".equals(name)) {
ColleagueA colleague = (ColleagueA)super.colleagues.get("ColleagueA");
colleague.self();
}else {
ColleagueB colleague = (ColleagueB)super.colleagues.get("ColleagueB");
colleague.self();
}
}else { //与其他同事合?nbsp;
if("ColleagueA".equals(name)) {
ColleagueA colleague = (ColleagueA)super.colleagues.get("ColleagueA");
colleague.out();
}else {
ColleagueB colleague = (ColleagueB)super.colleagues.get("ColleagueB");
colleague.out();
}
}
}
}
//试c?
public class Client {
public static void main(String[] args) {
//创徏一个中介?nbsp;
AbstractMediator mediator = new Mediator();
//创徏两个同事
ColleagueA colleagueA = new ColleagueA(mediator);
ColleagueB colleagueB = new ColleagueB(mediator);
//中介者分别与每个同事建立联系
mediator.addColleague("ColleagueA", colleagueA);
mediator.addColleague("ColleagueB", colleagueB);
//同事们开始工?nbsp;
colleagueA.self();
colleagueA.out();
System.out.println("======================合作愉快QQ务完成!\n");
colleagueB.self();
colleagueB.out();
System.out.println("======================合作愉快QQ务完成!");
}
}
试l果Q?
同事A --> 做好自己分内的事?...
同事A --> h同事B做好分内工作 ...
同事B --> 做好自己分内的事?...
======================合作愉快QQ务完成!
同事B --> 做好自己分内的事?...
同事B --> h同事A做好分内工作 ...
同事A --> 做好自己分内的事?...
======================合作愉快QQ务完成!
虽然以上代码中只有两个具体同事类Qƈ且测试类中也只是创徏了两个同事,但是q些我们都可以根据中介者模式的宗旨q行适当地扩展,卛_加具体同事类Q然后中介者就得担负更加重的Q务了。ؓ(f)啥?我们看到上面具体中介者类Mediator中的execute()Ҏ(gu)中现在就有一堆冗长的判断代码了。虽然可以把它分解ƈ增加到MediatorcM的其它privateҎ(gu)中,但是具体的业务逻辑是少不了的?br />
所以,在解耦同事类之间的联pȝ同时Q中介者自w也不免dq重Q因为几乎所有的业务逻辑都交代到中介者n上了Q可谓是“万众期待”的一个角色了。这是中介者模式的不之处??
此外Q上面这个代码例子是相当理想的了Q有时候我们根本抽取不?#8220;同事”之间的共性来形成一个AbstractColleague抽象同事c,q也大大增加了中介者模式的使用隑ֺ?nbsp;
׃上面代码实现中存?双向兌暴露在App?的不之?修改上面代码Q如下:(x)
修改后的同事cLQ?/font>
//抽象同事c?
abstract class AbstractColleague {
protected AbstractMediator mediator;
//舍去在构造函C建立起与中介者的联系
//public AbstractColleague(AbstractMediator mediator) {
// this.mediator = mediator;
//}
// 在抽象同事类中添加用于与中介者取得联p(x册)的方?nbsp;
public void setMediator(AbstractMediator mediator) {
this.mediator = mediator;
}
}
//具体同事A
class ColleagueA extends AbstractColleague {
//舍去在构造函C建立起与中介者的联系
// public ColleagueA(AbstractMediator mediator) {
// super(mediator);
// }
//每个具体同事必然有自己分内的事,没必要与外界相关?nbsp;
public void self() {
System.out.println("同事A --> 做好自己分内的事?...");
}
//每个具体同事L需要与外界交互的操作,通过中介者来处理q些逻辑q安排工?nbsp;
public void out() {
System.out.println("同事A --> h同事B做好分内工作 ...");
super.mediator.execute("ColleagueB", "self");
}
}
//具体同事B
class ColleagueB extends AbstractColleague {
//舍去在构造函C建立起与中介者的联系
// public ColleagueB(AbstractMediator mediator) {
// super(mediator);
// }
public void self() {
System.out.println("同事B --> 做好自己分内的事?...");
}
public void out() {
System.out.println("同事B --> h同事A做好分内工作 ...");
super.mediator.execute("ColleagueA", "self");
}
}
修改后的中介者:(x)
//抽象中介?
abstract class AbstractMediator {
//中介者肯定需要保持有若干同事的联pL?nbsp;
protected Hashtable<String, AbstractColleague> colleagues = new Hashtable<String, AbstractColleague>();
//中介者可以动态地与某个同事徏立联p?nbsp;
public void addColleague(String name, AbstractColleague c) {
// 在中介者这里帮助具体同事徏立v于中介者的联系
c.setMediator(this);
this.colleagues.put(name, c);
}
//中介者也可以动态地撤销与某个同事的联系
public void deleteColleague(String name) {
this.colleagues.remove(name);
}
//中介者必d备在同事之间处理逻辑、分配Q务、促q交的操作
public abstract void execute(String name, String method);
}
//试c?
public class Client {
public static void main(String[] args) {
//创徏一个中介?nbsp;
AbstractMediator mediator = new Mediator();
//不用构造函Cؓ(f)具体同事注册中介者来取得联系?
// ColleagueA colleagueA = new ColleagueA(mediator);
// ColleagueB colleagueB = new ColleagueB(mediator);
ColleagueA colleagueA = new ColleagueA();
ColleagueB colleagueB = new ColleagueB();
//中介者分别与每个同事建立联系
mediator.addColleague("ColleagueA", colleagueA);
mediator.addColleague("ColleagueB", colleagueB);
//同事们开始工?nbsp;
colleagueA.self();
colleagueA.out();
System.out.println("======================合作愉快QQ务完成!\n");
colleagueB.self();
colleagueB.out();
System.out.println("======================合作愉快QQ务完成!");
}
} 试之后的结果与修改前一栗?br />
Interpreter模式的概?/font>
Interpreter是一U特D的设计模式Q它建立一个解释器Q对于特定的计算机程序设计语aQ用来解释预先定义的文法。简单地_(d)Interpreter模式是一U简单的语法解释器构架?/font>
Interpreter模式有很多种实现Ҏ(gu)Q下面我们给出Interpreter模式的一U类图来说明Interpreter模式Q?/font>
在上图中Q我们假N要在Client中解释某文法QClient调用Context来存储文法规则,q调用解释器AbstractionExpressioncL(wi)来对该文法加以解释。注意,上图只是Interpreter模式的一U实现方式的cd?/font>
Context
解释器上下文环境cR用来存储解释器的上下文环境Q比如需要解释的文法{?br />
AbstractExpression
解释器抽象类?br />
ConcreteExpression
解释器具体实现类?/p>
Interpreter模式的实现范?br />
Z帮助大家理解Interpreter模式的基本概念,我们在这里只举一个最单的例子?br />
让一个表辑ּal过PlusExpression解释器处理后使该表达?1Q经qMinusExpression解释器处理后使该表达?1?/p>
代码Q?/p>
import java.util.ArrayList;
import java.util.List;
public class Client {
public static void main(String []args) {
String inputExpr = "10";
Context context = new Context(inputExpr);
List list = new ArrayList();
list.add(new PlusExpression());
list.add(new PlusExpression());
list.add(new MinusExpression());
list.add(new MinusExpression());
list.add(new MinusExpression());
for (int i=0;i<list.size();i++) {
AbstractExpression expression = (AbstractExpression)list.get(i);
expression.interpret(context);
}
System.out.println(context.getOutput());
}
}
/**
* Context
*
*/
class Context {
private String input;
private int output;
public Context (String input) {
this. input = input;
}
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
public int getOutput() {
return output;
}
public void setOutput(int output) {
this.output = output;
}
}
/**
* Expression & subclass
*
*/
abstract class AbstractExpression {
public abstract void interpret(Context context);
}
class PlusExpression extends AbstractExpression {
public void interpret(Context context) {
System.out.println("PlusExpression ++");
String input = context.getInput();
int parsedResult = Integer.parseInt(input);
parsedResult ++;
context.setInput(String.valueOf(parsedResult));
context.setOutput(parsedResult);
}
}
class MinusExpression extends AbstractExpression {
public void interpret(Context context) {
System.out.println("PlusExpression --");
String input = context.getInput();
int parsedResult = Integer.parseInt(input);
parsedResult --;
context.setInput(String.valueOf(parsedResult));
context.setOutput(parsedResult);
}
}
q行q显CClientQ?/p>
C:\Interpreter>javac *.java
C:\Interpreter>java Client
PlusExpression ++
PlusExpression ++
PlusExpression --
PlusExpression --
PlusExpression --
9
C:\Interpreter>
2.我们具体来看一个例子:(x)
首先我们要完成对命o(h)的对象封装:(x)
public interface Command {
public void execute();
}
只有一个方法,所有的具体命o(h)的对象都要实现这个接口,q就做到了封装,比如对于灯这个对象,
public class Light {
public Light() {
}
public void on() {
System.out.println("Light is on");
}
public void off() {
System.out.println("Light is off");
}
}
我们可以通过上述接口装“开?#8221;q个命o(h)Q这个就是所谓的命o(h)对象Q它把动作和接收者包q对象之中,只暴露一个executeҎ(gu)Q?/font>
public class LightOnCommand implements Command {
Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.on();
}
}
而我们的遥控器对于上q封装要一无所知,q样才能做到解耦:(x)
public class SimpleRemoteControl {
Command slot;
public SimpleRemoteControl() {}
public void setCommand(Command command) {
slot = command;
}
public void buttonWasPressed() {
slot.execute();
}
}
我们现在试着用一下这个遥控器Q我们首先创Z个遥控器Q然后创建开灯这个命令ƈ|于其中Q最后按下按键,q样Q遥控器不知道是哪个对象Q实际上是灯Q进行了哪个动作Q实际上是开灯这个动作)可以完成请求的发出。如此一来,遥控器和灯之间的耦合性就非常低了Q?/font>
public class RemoteControlTest {
public static void main(String[] args) {
SimpleRemoteControl remote = new SimpleRemoteControl();
Light light = new Light();
LightOnCommand lightOn = new LightOnCommand(light);
remote.setCommand(lightOn);
remote.buttonWasPressed();
}
}
很简单的Q我们想要在一个遥控机中实现控制多个家늚能力可以用一个数l来l护q样的一l命令,可以看做提供了多个命令的插槽Q?/font>
public class RemoteControl {
Command[] onCommands;
Command[] offCommands;
public RemoteControl() {
onCommands = new Command[7];
offCommands = new Command[7];
Command noCommand = new NoCommand();
for (int i = 0; i < 7; i++) {
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
}
public void setCommand(int slot, Command onCommand, Command offCommand) {
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
public void onButtonWasPushed(int slot) {
onCommands[slot].execute();
}
public void offButtonWasPushed(int slot) {
offCommands[slot].execute();
}
public String toString() {
}
}
你可能会(x)注意C个叫做noCommand的对象出现在初始化遥控器对象时。这个是个小技巧,我们q不xơ都查某个插槽是不是都绑定了命o(h)Q那么我们就引入了这个东东,实现一个不做事情的命o(h)Q?/font>
public class NoCommand implements Command {
public void execute() { }
}
q样初始化就让每一个插槽都有命令了Q以后若不进一步指明命令的插槽Q那么就是默认这个noCommand对象。这是一个典型的“I对?#8221;例子Q当你不惌回一个有意义的对象时Q空对象十分有用,此时I对象可以做成判断NULL或者提C?#8220;未绑?#8221;信息的工作?/font>
3.我们在命令模式中也可以设|类似undo的撤销命o(h)来撤销发出的命令请求:(x)
public interface Command {
public void execute();
public void undo();
}
我们q是拿开늁q个操作来说明,毕竟q够简单:(x)对于一个开灯命令,其对应的撤销命o(h)自然?#8220;关电(sh)?#8221;Q?/font>
public class LightOnCommand implements Command {
Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.on();
}
public void undo() {
light.off();
}
}
虽然q简单之极,q还没完Q我们必让遥控器记住上ơ到底做了什么操作,才可能去撤销Q?/font>
package headfirst.command.undo;
import java.util.*;
//
// This is the invoker
//
public class RemoteControlWithUndo {
Command[] onCommands;
Command[] offCommands;
Command undoCommand;
public RemoteControlWithUndo() {
onCommands = new Command[7];
offCommands = new Command[7];
Command noCommand = new NoCommand();
for(int i=0;i<7;i++) {
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
undoCommand = noCommand;
}
public void setCommand(int slot, Command onCommand, Command offCommand) {
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
public void onButtonWasPushed(int slot) {
onCommands[slot].execute();
undoCommand = onCommands[slot];//记录操作动作
}
public void offButtonWasPushed(int slot) {
offCommands[slot].execute();
undoCommand = offCommands[slot];//记录操作动作
}
public void undoButtonWasPushed() {
undoCommand.undo();//发出撤销命o(h)
}
public String toString() {
}
}
对于늁Q我们只有两U状态,但是要是多个状态呢Q我们D一个吊扇的例子Q吊扇有多个转速,高中低关Q四个状态:(x)
public class CeilingFan {
String location = "";
int level;
public static final int HIGH = 2;
public static final int MEDIUM = 1;
public static final int LOW = 0;
public CeilingFan(String location) {
this.location = location;
}
public void high() {
// turns the ceiling fan on to high
level = HIGH;
System.out.println(location + " ceiling fan is on high");
}
public void medium() {
// turns the ceiling fan on to medium
level = MEDIUM;
System.out.println(location + " ceiling fan is on medium");
}
public void low() {
// turns the ceiling fan on to low
level = LOW;
System.out.println(location + " ceiling fan is on low");
}
public void off() {
// turns the ceiling fan off
level = 0;
System.out.println(location + " ceiling fan is off");
}
public int getSpeed() {
return level;
}
}
那么在实现风扇各个{速命令时Q就要去记录在执行这个命令前风扇的{速,其撤销命o(h)中则Ҏ(gu)记录的部分完成undo操作?/font>
public class CeilingFanHighCommand implements Command {
CeilingFan ceilingFan;
int prevSpeed;
public CeilingFanHighCommand(CeilingFan ceilingFan) {
this.ceilingFan = ceilingFan;
}
public void execute() {
prevSpeed = ceilingFan.getSpeed();
ceilingFan.high();
}
public void undo() {
switch (prevSpeed) {
case CeilingFan.HIGH: ceilingFan.high(); break;
case CeilingFan.MEDIUM: ceilingFan.medium(); break;
case CeilingFan.LOW: ceilingFan.low(); break;
default: ceilingFan.off(); break;
}
}
}
当然你也可以实现诸如多个命o(h)扚w执行和完成多个撤销操作?/font>
命o(h)模式可以用于工作队列和日志操作等斚w?/font>
昄Q击鼓传q合责任链模式的定义。参加游戏的人是一个个的具体处理者对象,击鼓的h便是客户端对象。花代表酒o(h)Q是传向处理者的hQ每一个参加游戏的人在接到传来的花Ӟ可选择的行为只有两个:(x)一是将花向下传Q一是执行酒?--喝酒。一个h不能既执行酒令,又向下家传花Q当某一个h执行了酒令之后,游戏重新开始。击鼓的人ƈ不知道最l是由哪一个做游戏的h执行酒o(h)Q当然执行酒令的人必然是做游戏的Z中的一个?/font>
击鼓传花的类囄构如下:(x)
?、击鼓传ql的cd定义?/font>
单独考虑击鼓传花pȝQ那么像贾母、贾赦、贾ѝ贾宝玉和贾环等传花者均应当?#8220;具体传花?#8221;的对象,而不应当是单独的c;但是责Q链模式往往是徏立在现有pȝ的基之上的,因此铄l构和组成不p任链模式本n军_?/font>
pȝ的分?br />
在《红楼梦》第七十五回里生动地描述了贾府里的一场击鼓传花游戏:(x)“贾母坐下Q左垂首贾Q贾珍,贄Q贾蓉,叛_首贾政,宝玉Q贾环,贑օQ团团围坐?..贾母便命折一枝桂花来Q命一媛_在屏后击鼓传花。若花到谁手中,饮酒一?..于是先从贾母Pơ贾赦,一一接过。鼓C转,恰恰在贾政手中住了,只得饮了酒?#8221;q场游戏接着又把׃C宝玉和贾赦手里,接着又传C在贾环手?..
如果用一个对象系l描q贾府,那么贾母、贾赦、贾ѝ贾宝玉和贾环等{就应当分别׃个个具体cM表,而这场击鼓传花游戏的cdQ按照责任链模式Q应当如下图所C:(x)
?、贾府这ơ击鼓传qC意性对象图?
可以看出Q击鼓传花游戏满任链模式的定义,是纯的责任链模式的例?
Javapȝ的解下面的类囄Zq些cȝ具体接口设计。读者不隄出,DrumBeaterQ击鼓者)、PlayerQ传p)、JiaMuQ贾母)、JiaSheQ贾赦)、JiaZhengQ贾政)、JiaBaoYuQ宝玉)、JiaHuanQ贾环){组成这个系l?br />
?、击鼓传qcd完全W合责Q链模式的定义?
下面是客L(fng)cDrumBeater的源代码Q?/font>
//DrumBeater的源代码
public class DrumBeater
{
private static Player player;
static public void main(String[] args)
{
player = new JiaMu( new JiaShe( new JiaZheng( new JiaBaoYu(new JiaHuan(null)))));
player.handle(4);
}
}
//抽象传花者Playcȝ源代?br />
abstract class Player
{
abstract public void handle(int i);
private Player successor;
public Player() { successor = null;}
protected void setSuccessor(Player aSuccessor)
{
successor = aSuccessor;
}
public void next(int index)
{
if( successor != null )
{
successor.handle(index);
}
else
{
System.out.println("Program terminated.");
}
}
}
}
抽象cPlayerl出了两个方法的实现Q以格式setSuccessor()Q另一个是next()。前者用来设|一个传p对象的下家Q后者用来将酒o(h)传给下家。PlayercȝZ一个抽象方法handle()Q代表执行酒令?/font>
下面的这些具体传p类给出handle()Ҏ(gu)的实现?/font>
//贾母的JiaMuc?br />
class JiaMu extends Player
{
public JiaMu(Player aSuccessor)
{
this.setSuccessor(aSuccessor);
}
public void handle(int i)
{
if( i == 1 )
{
System.out.println("Jia Mu gotta drink!");
}
else
{
System.out.println("Jia Mu passed!");
next(i);
}
}
}
//贾的JiaShec?br />
class JiaShe extends Player
{
public JiaShe(Player aSuccessor)
{
this.setSuccessor(aSuccessor);
}
public void handle(int i)
{
if( i == 2 )
{
System.out.println("Jia She gotta drink!");
}
else
{
System.out.println("Jia She passed!");
next(i);
}
}
}
//贾政的JiaZhengc?br />
class JiaZheng extends Player
{
public JiaZheng(Player aSuccessor)
{
this.setSuccessor(aSuccessor);
}
public void handle(int i)
{
if( i == 3 )
{
System.out.println("Jia Zheng gotta drink!");
}
else
{
System.out.println("Jia Zheng passed!");
next(i);
}
}
}
//贑֮玉的JiaBaoYuc?br />
class JiaBaoYu extends Player
{
public JiaBaoYu(Player aSuccessor)
{
this.setSuccessor(aSuccessor);
}
public void handle(int i)
{
if( i == 4 )
{
System.out.println("Jia Bao Yu gotta drink!");
}
else
{
System.out.println("Jia Bao Yu passed!");
next(i);
}
}
}
//JiaHuanc?br />
class JiaHuan extends Player
{
public JiaHuan(Player aSuccessor)
{
this.setSuccessor(aSuccessor);
}
public void handle(int i)
{
if( i == 5 )
{
System.out.println("Jia Huan gotta drink!");
}
else
{
System.out.println("Jia Huan passed!");
next(i);
}
}
}
可以看出QDrumBeater讑֮了责任链的成员和他们的顺序:(x)责Q铄贾母开始到贄Q周而复始。JiaMucRJiaShecRJiaZhengcRJiaBaoYucMJiaHuancd是抽象传pPlayercȝ子类?/font>
本节所实现的DrumBeatercd把请求传l贾母时Q实际上指定了由4号传p处理酒令。虽然DrumBeaterq不知道哪一个传p类持有L(fng)4Q但是这个号码在本系l一开始就写死的。这当然q不W合击鼓传花游戏的精,因ؓ(f)q个游戏实际上要求有两个同时q行的过E:(x)击鼓q程和传pE。击鼓应当是定时停止的,当击鼓停止时Q执行酒令者就定了。但是本节这样做可以佉K题得到简化ƈ读者的_֊攑֜责Q链模式上Q而不是两个过E的处理上?br />
在什么情况下使用责Q链模?/font>
在下面的情况下用责任链模式Q?/font>
W一、系l已l有一个由处理者对象组成的链。这个链可能由复合模式给出,
W一、当有多于一个的处理者对象会(x)处理一个请求,而且在事先ƈ不知道到底由哪一个处理者对象处理一个请求。这个处理者对象是动态确定的?/font>
W二、当pȝ惛_Z个请求给多个处理者对象中的某一个,但是不明显指定是哪一个处理者对象会(x)处理此请求?/font>
W三、当处理一个请求的处理者对象集合需要动态地指定时?/font>
使用责Q链模式的长处和短?/font>
责Q链模式减低了发出命o(h)的对象和处理命o(h)的对象之间的耦合Q它允许多与一个的处理者对象根据自q逻辑来决定哪一个处理者最l处理这个命令。换a之,发出命o(h)的对象只是把命o(h)传给铄构的起始者,而不需要知道到底是链上的哪一个节点处理了q个命o(h)?/font>
昄Q这意味着在处理命令上Q允许系l有更多的灵zL。哪一个对象最l处理一个命令可以因为由那些对象参加责Q链、以?qing)这些对象在责Q链上的位|不同而有所不同?/font>
责Q链模式的实现
铄构的由来
值得指出的是Q责任链模式q不创徏任链。责任链的创建必Lpȝ的其它部分完成?/font>
责Q链模式减低了h的发送端和接收端之间的耦合Q多个对象都有Z(x)处理q个h。一个链可以是一条线Q一个树(wi)Q也可以是一个环。链的拓扑结构可以是单连通的或多q通的Q责任链模式q不指定责Q铄拓扑l构。但是责任链模式要求在同一个时间里Q命令只可以被传l一个下Ӟ或被处理掉)Q而不可以传给多于一个下家。在下面的图中,责Q链是一个树(wi)l构的一部分?/font>
?、责任链是系l已有的?wi)结构的一部分。图中有阴媄的对象给Z一个可能的命o(h)传播路径?/font>
责Q铄成员往往是一个更大的l构的一部分。比如在前面所讨论的《红楼梦》中击鼓传花的游戏中Q所有的成员都是贑ֺ的成员。如果责任链的成员不存在Q那么ؓ(f)了用责任链模式Q就必须创徏它们Q责任链的具体处理者对象可以是同一个具体处理者类的实例?/font>
SingletonQ用于单U程应用E序
public class Singleton {
private Singleton(){}
//在自己内部定义自׃个实例,是不是很奇怪?
//注意q是private 只供内部调用
private static Singleton instance = new Singleton();
//q里提供了一个供外部讉K本class的静态方法,可以直接讉K
public static Singleton getInstance() {
return instance;
}
}
Double-Checked LockingQ用于多U程应用E序
public class Singleton {
private static Singleton instance = null;
public static synchronized Singleton getInstance() {
//q个Ҏ(gu)比上面有所改进Q不用每ơ都q行生成对象Q只是第一ơ
//使用时生成实例,提高了效率!
if (instance==null)
instanceQnew Singleton();
return instance; }
}
关于对象的复Ӟ我们需要考虑以下两个问题Q?/font>
1.对象实时状态的复制Q?/font>
2.对象引用成员变量的复制?/font>
如果通过new方式来实例化对象Q只能得到对象的初始状态,q显然不行。在Java中,所有类都承于Objectc,而Object有一个clone()Ҏ(gu)。通过查看JDK文Q该clone()Ҏ(gu)虽然能够实现实时状态的复制Q解决了问题1Q,但是只能实现“拷?#8221;Q即只能实现基本数据cdQ包含StringQ的拯Q不能实现引用数据类型的拯Qƈ不能满原型模式的要求(问题2不能得到解决Q?/font>
怎么办?重写cȝclone()Ҏ(gu)Q通过对象的写入写出来实现“深拷?#8221;和实时状态复制。重写(OverrideQ需要遵循的条g是:(x)Ҏ(gu)名,参数列表和父cM_(d)q回值是父类q回值的子类。重写cloneҎ(gu)完全满了条件。(无参敎ͼҎ(gu)名一_(d)q回的所有类都是Object的子c;当然可以不重写,Ҏ(gu)名改用其它,但是不徏议。)
可以惌Q若一个类含有若干个引用数据类型(c)Q而该引用数据cdQ类Q中又含有若q个引用数据cd…q样嵌套下去Q需要复制的׃是一个类Q而是一?#8220;cL(wi)”Q树(wi)l构Q。Java通过对象序列化到文件或内存Q底层封装了对类?wi)的遍历和复制过E。这个机制已l封装,我们无需了解?/p>
考虑q样一个应用场景:(x)
一个RPG游戏Q英雄类含有两个成员变量Q一个是英雄名字Qؓ(f)普通成员变量;一个是英雄属性,为引用成员变量(装了英雄的状态,如生命倹{魔法|。ؓ(f)了实现对象实时拷贝的高效率,我们选择对象序列化到内存(不是文gQ硬盘)Q。通过输入输出实现对象的“深拷?#8221;?/p>
具体的实C码如下:(x)
package com.csufox.Prototype;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
class Property implements java.io.Serializable{
int blood = 100; //生命?br />
int magic = 100; //法?br />
}
class Hero implements java.io.Serializable{ //需要实现序列化接口
String name = "英雄";
Property property = new Property(); //含有引用成员Q需?#8220;?#8221;拯
public Hero clone(){
Hero copy = null;
try{
//实例化ObjectOutputStreamQ写入字节数l输出流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
byte[] data = baos.toByteArray(); //data是字节数l,存储着对象的拷?/p>
//实例化ObjectInputStreamQ从字节数组输入中d对象
ByteArrayInputStream bais = new ByteArrayInputStream(data);
ObjectInputStream ois = new ObjectInputStream(bais);
copy = (Hero)ois.readObject();
}catch(Exception ex){ex.printStackTrace();}
return copy;
}
}
public class Prototype {
public static void main(String[] args) throws Exception {
Hero hero = new Hero();
hero.property.blood -= 20; //原英雄状态发生改变,生命值减?0
Hero copy = hero.clone();
System.out.println(copy.property.blood); //输出“80”Q说明实C实时拯和深拯
}
}
׃qC码可知,原型模式h以下特点Q?/p>
实现Serializable序列化接口;
在类中重写clone()Ҏ(gu)Q由O(jin)bjectl承而来Q;
通过Java I/0Q将对象写入内存Q然后再从内存读取(反序列化Q?
Builder模式是一步一步创Z个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容可以构建它?用户不知道内部的具体构徏l节.Builder模式是非常类似抽象工厂模?l微的区别大概只有在反复使用中才能体?x)?
Z使用?
是ؓ(f)了将构徏复杂对象的过E和它的部g解?注意: 是解耦过E和部g.
因ؓ(f)一个复杂的对象,不但有很多大量组成部?如汽?有很多部?车轮 方向?发动有各U小零g{等,部g很多,但远不止q些,如何这些部件装配成一辆汽?q个装配q程也很复杂(需要很好的l装技?,Builder模式是Z部件和l装q程分开.
如何使用?
首先假设一个复杂对象是由多个部件组成的,Builder模式是把复杂对象的创建和部g的创建分别开?分别用BuildercdDirectorcL表示.
首先,需要一个接?它定义如何创建复杂对象的各个部g:
public interface Builder {
//创徏部gA 比如创徏汽R车轮
void buildPartA();
//创徏部gB 比如创徏汽R方向?br />
void buildPartB();
//创徏部gC 比如创徏汽R发动?br />
void buildPartC();
//q回最后组装成品结?(q回最后装配好的汽?
//成品的组装过E不在这里进?而是转移C面的DirectorcMq行.
//从而实C解耦过E和部g
Product getResult();
}
用Director构徏最后的复杂对象,而在上面Builder接口中封装的是如何创Z个个部g(复杂对象是由q些部gl成?,也就是说Director的内Ҏ(gu)如何部件最后组装成成品:
public class Director {
private Builder builder;
public Director( Builder builder ) {
this.builder = builder;
}
// 部件partA partB partC最后组成复杂对?br />
//q里是将车轮 方向盘和发动机组装成汽R的过E?br />
public void construct() {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
}
}
Builder的具体实现ConcreteBuilder:
通过具体完成接口Builder来构建或装配产品的部?
定义q明它所要创建的是什么具体东?
提供一个可以重新获取品的接口:
public class ConcreteBuilder implements Builder {
Part partA, partB, partC;
public void buildPartA() {
//q里是具体如何构建partA的代?/font>
};
public void buildPartB() {
//q里是具体如何构建partB的代?br />
};
public void buildPartC() {
//q里是具体如何构建partB的代?br />
};
public Product getResult() {
//q回最后组装成品结?br />
};
}
复杂对象:产品Product:
public interface Product { }
复杂对象的部?
public interface Part { }
我们看看如何调用Builder模式:
ConcreteBuilder builder = new ConcreteBuilder();
Director director = new Director( builder );
director.construct();
Product product = builder.getResult();
Builder模式的应?br /> 在Java实际使用?我们l常用到"?(Pool)的概?当资源提供者无法提供够的资源,q且q些资源需要被很多用户反复׃n?需要用池.
"?实际是一D内?当池中有一些复杂的资源?断肢"(比如数据库的q接?也许有时一个连接会(x)中断),如果循环再利用这?断肢",提高内存用效?提高池的性能.修改Builder模式中DirectorcM之能诊断"断肢"断在哪个部g?再修复这个部?
FactoryMethod是一U创建性模?它定义了一个创建对象的接口,但是却让子类来决定具体实例化哪一个类.当一个类无法预料要创建哪U类的对象或是一个类需要由子类来指定创建的对象时我们就需要用到Factory Method 模式?单说?Factory Method可以Ҏ(gu)不同的条件生不同的实例,当然q些不同的实例通常是属于相同的cd,h共同的父c?Factory Method把创些实例的具体q程装h?化了客户端的应用,也改善了E序的扩展?使得来可以做最的改动可以加入新的待创徏的类. 通常我们Factory Method作ؓ(f)一U标准的创徏对象的方?当发现需要更多的灉|性的时?开始考虑向其它创建型模式转化
单分?/p>
?是Factory Method 模式的结构图,q里提供了一些术?让我们可以进行更方便的描q?
Product: 需要创建的产品的抽象类.
ConcreteProduct: Product的子c?一pd具体的?
Creator: 抽象创徏器接?声明q回Productcd对象的Factory Method.
ConcreteCreator: 具体的创建器,重写C(j)reator中的Factory Method,q回ConcreteProductcd的实?
?: Factory Method 模式l构
由此可以清楚的看L(fng)q对应关系: Product <====> Creator ; ConreteProduct <====> ConreteCreator
抽象产品对应抽象创徏?具体产品对应具体创徏?q样做的好处是什么呢?Z么我们不直接用具体的产品和具体的创徏器完成需求呢?实际上我们也可以q样?但通过Factory Method模式来完?客户(client)只需引用抽象的Product和Creater,对具体的ConcreteProduct和ConcreteCreator可以毫不兛_,q样做我们可以获得额外的好处:
首先客户端可以统一从抽象创建器获取产生的实?Creator的作用将client和品创E分d?客户不用操心q回的是那一个具体的产品,也不用关心这些品是如何创徏?同时,ConcreteProduct也被隐藏在Product后面,ConreteProductl承了Product的所有属?q实CProduct中定义的抽象Ҏ(gu),按照Java中的对象造型(cast)原则,通过ConcreteCreator产生的ConcreteProduct可以自动的上溯造型成Product.q样一?实质内容不同的ConcreteProduct可以在形式上统一为Product,通过Creator提供lclient来访?
其次,当我们添加一个新的ConcreteCreator?׃Creator所提供的接口不?客户端程序不?x)有丝毫的改?不会(x)带来动一发而牵全n的灾? q就是良好封装性的体现.但如果直接用ConcreteProduct和ConcreteCreator两个cL无论如何也做不到q点? 优良的面向对象设计鼓׃用封?encapsulation)和委?delegation),而Factory Method模式是使用了封装和委托的典型例?q里装是通过抽象创徏器Creator来体现的,而委托则是通过抽象创徏器把创徏对象的责d全交l具体创建器ConcreteCreator来体现的.
现在,请再回头看看基本概念中的那段?开始也许觉得生涩难?现在是不是已l明朗化了很?
下面让我们看看在 Java 中如何实现Factory Method模式,q一步加深对它的认识.
具体实施
先说明一?用Factory Method模式创徏对象q不一定会(x)让我们的代码更短,实事上往往更长,我们也用了更多的类,真正的目的在于这样可以灵zȝ,有弹性的创徏不确定的对象.而且,代码的可重用性提高了,客户端的应用化了,客户E序的代码会(x)大大减少,变的更具可读?
标准实现: q里我采用Bruce Eckel 用来描述OO思想的经怾?Shape.q样大家?x)比较熟?zhn)一?我完全按照图1中所定义的结构写了下面的一D|CZ?q段代码的作用是创徏不同的Shape实例,每个实例完成两个操作:draw和erase.具体的创E委托ShapeFactory来完?
1.a 首先定义一个抽象类Shape,定义两个抽象的方?
abstract class Shape {
// 勄shape
public abstract void draw();
// 擦去 shape
public abstract void erase();
public String name;
public Shape(String aName){
name = aName;
}
}
1.b 定义 Shape的两个子c? Circle, Square,实现Shape中定义的抽象Ҏ(gu)
// 圆Ş子类
class Circle extends Shape {
public void draw() {
System.out.println("It will draw a circle.");
}
public void erase() {
System.out.println("It will erase a circle.");
}
// 构造函?br />
public Circle(String aName){
super(aName);
}
}
// 方Ş子类
class Square extends Shape {
public void draw() {
System.out.println("It will draw a square.");
}
public void erase() {
System.out.println("It will erase a square.");
}
// 构造函?br />
public Square(String aName){
super(aName);
}
}
1.c 定义抽象的创建器,anOperation调用factoryMethod创徏一个对?q对该对象进行一pd操作.
abstract class ShapeFactory {
protected abstract Shape factoryMethod(String aName);
// 在anOperation中定义Shape的一pd行ؓ(f)
public void anOperation(String aName){
Shape s = factoryMethod(aName);
System.out.println("The current shape is: " + s.name);
s.draw();
s.erase();
}
}
1.d 定义与circle和square相对应的两个具体创徏器CircleFactory,SquareFactory,实现父类的methodFactoryҎ(gu)
// 定义q回 circle 实例?CircleFactory
class CircleFactory extends ShapeFactory {
// 重蝲factoryMethodҎ(gu),q回Circle对象
protected Shape factoryMethod(String aName) {
return new Circle(aName + " (created by CircleFactory)");
}
}
// 定义q回 Square 实例?SquareFactory
class SquareFactory extends ShapeFactory {
// 重蝲factoryMethodҎ(gu),q回Square对象
protected Shape factoryMethod(String aName) {
return new Square(aName + " (created by SquareFactory)");
}
}
1.e 试c?h意这个客L(fng)E序多么z?既没有罗嗦的条g判断语句,也无需兛_ConcreteProduct和ConcreteCreator的细?因ؓ(f)q里我用anOperation装了Product里的两个Ҏ(gu),所以连Product的媄子也没看?当然把Product里方法的具体调用攑ֈ客户E序中也是不错的).
class Main {
public static void main(String[] args){
ShapeFactory sf1 = new SquareFactory();
ShapeFactory sf2 = new CircleFactory();
sf1.anOperation("Shape one");
sf2.anOperation("Shape two");
}
}
q行l果如下:
The current shape is: Shape one (created by SquareFactory)
It will draw a square.
It will erase a square.
The current shape is: Shape two (created by CircleFactory)
It will draw a circle.
It will erase a circle.
参数化的Factory Method: q种方式依靠指定的参C为标志来创徏对应的实?q是很常见的一U办?比如JFC中的BorderFactory是个很不错的例? 以下的这个例子是用字W串作ؓ(f)标记来进行判断的,如果参数的类型也不一?那就可以用到q蝲函数来解册个问?定义一pd参数和方法体不同的同名函?q里java.util.Calendar.getInstance()又是个极好的例子.参数化的创徏方式克服了Factory Method模式一个最显著的缺?是当具体品比较多?我们不得不也建立一pd与之对应的具体构造器. 但是在客L(fng)我们必须指定参数来决定要创徏哪一个类.
2.a 我们在第一U方法的基础上进行修?首先自定义一个的异常,q样当传入不正确的参数时可以得到更明昄报错信息.
class NoThisShape extends Exception {
public NoThisShape(String aName) {
super(aName);
}
}
2.bL了ShapeFactory的两个子c?改ؓ(f)由ShapeFactory直接负责实例的创? ShapeFactory自己变成一个具体的创徏?直接用参数化的方法实现factoryMethodq回多种对象.
abstract class ShapeFactory {
private static Shape s;
private ShapeFactory() {}
static Shape factoryMethod(String aName, String aType) throws NoThisShape{
if (aType.compareTo("square")==0)
return new Square(aName);
else if (aType.compareTo("circle")==0)
return new Circle(aName);
else throw new NoThisShape(aType);
}
// 在anOperation中定义Shape的一pd行ؓ(f)
static void anOperation(String aName, String aType) throws NoThisShape{
s = factoryMethod(aName, aType);
System.out.println("The current shape is: " + s.name);
s.draw();
s.erase();
}
}
2.c 试c?q里客户端必L定参数来军_具体创徏哪个c?q个例子里的anOperation是静态函?可以直接引用.
class Main {
public static void main(String[] args) throws NoThisShape{
ShapeFactory.anOperation("Shape one","circle");
ShapeFactory.anOperation("Shape two","square");
ShapeFactory.anOperation("Shape three", "delta");
}
}
q行l果如下:
The current shape is: Shape one
It will draw a circle.
It will erase a circle.
The current shape is: Shape two
It will draw a square.
It will erase a square.
Exception in thread "main" NoThisShape: delta
at ShapeFactory.factoryMethod(ShapeFactory.java:10)
at ShapeFactory.anOperation(ShapeFactory.java:15)
at Main.main(Main.java:5)
动态装载机?
有的时候我们会(x)把ConcreteProduct的实例传l创建器作ؓ(f)参数,q种情况?如果在创建器里完成创E?必d断参数的具体cd(用instanceof),然后才能产生相应的实?那么比较好的做法是利用Java的动态装载机制来完成qg?比如:
我们得到一个Shape的子cs,但不知道具体是那个子c?可以利用Classc自带的Ҏ(gu)newInstance()得到实例
return (Shape)s.getClass().newInstance();
q种Ҏ(gu)有兴得读者可以自己尝?限于幅,不写具体代码出来?
回页?
后话:
看完q篇文章?怿读者对Factory Method模式有一个比较清楚的了解?我想说的?我们不仅应该兛_一个具体的模式有什么作?如何d现这个模?更应该透过现象看本?不但知其?q要知其所以然.要通过Ҏ(gu)式的学习(fn)加深寚w向对象思想的理?让自q认识得到升华.Factory Method模式看似?实则深刻.抽象,装,l承,委托,多?针对接口~程{面向对象中的概念都在这里得C一一的体?只有抓住了它的本?我们才能够不拘于形式的灵z运?而不是ؓ(f)了用模式而用模?
一?Proxy模式定义:
为其他对象提供一U代理以控制q个对象的访问?br />
二?模式解说
Proxy代理模式是一U结构型设计模式Q主要解决的问题是:(x)在直接访问对象时带来的问题,比如_(d)(x)要访问的对象在远E的机器上。在面向对象pȝ中,有些对象׃某些原因Q比如对象创建开销很大Q或者某些操作需要安全控Ӟ或者需要进E外的访问)Q直接访问会(x)l用者或者系l结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的讉K?q个讉K层也叫代理。Proxy模式是最常见的模式,在我们生zM处处可见Q例如我们买火R不一定非要到火R站去乎ͼ可以C些火车票的代售点M。寄信不一定是自己dQ可以把信委托给邮局Q由邮局把信送到目的圎ͼ现实生活中还有很多这L(fng)例子Q就不一一列D了?
三?l构?br />
Proxy模式l构囑֦?
四?一个例?br />
举一个比较俗的例子,一个男孩boy喜欢上了一个女孩girlQ男孩一直想认识奛_Q直接去和女孩打招呼吧,又觉得不好意思(q个男孩比较害羞Q。于是男孩想Z一个办法,委托奛_的室友Proxyd他搞定这件事Q获得一些关于女孩的信息Q如有没有BF{?q就叫知q|才能百战不殆Q。下面给个例子的E序实现Q?/p>
interface GirlInfo{
public void hasBoyFriend();
}
class Girl implements GirlInfo{
public void hasBoyFriend(){
System.out.println("q没有男朋友");
}
}
class Proxy implements GirlInfo{
private GirlInfo girl;
public Proxy(GirlInfo girl){
this.girl=girl;
}
public void hasBoyFriend(){
this.girl.hasBoyFriend();
}
}
public class ProxyClient {
public static void main(String[] args) {
GirlInfo girl=new Girl();
Proxy proxy=new Proxy(girl);
proxy.hasBoyFriend();
}
}
从这个例子我们可以看出,Proxy模式是不是和Adapter模式差不多,都是调用一个已有对象的Ҏ(gu)来完成功能。但是他们之间还是有区别的,那就是Proxy模式的目标类必须要实现某个接口,代理cL有必要实现该接口Q模式是ȝQ它的实现是zȝQ如果一味的怿某些书上的实玎ͼ学习(fn)模式也就失去了意义。有的书上称q种实现为静态代理,之所以这hZ区别于Proxy模式在jdk中的另一U实?jdk中的实现UCؓ(f)动态代理。下面用jdk中的Ҏ(gu)l出q个例子的实现。代码如?
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface GirlInfo{
public void hasBoyFriend();
}
class Girl implements GirlInfo{
public void hasBoyFriend(){
System.out.println("q没有男朋友");
}
}
class GirlProxy implements InvocationHandler{
private Object delegate;
public Object bind(Object delegate){
this.delegate=delegate;
return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(delegate, args);
return null;
}
}
public class ProxyClient {
public static void main(String[] args) {
GirlProxy girlProxy=new GirlProxy();
GirlInfo girl=(GirlInfo)girlProxy.bind(new Girl());
girl.hasBoyFriend();
}
}
五?适用?br /> 1Q?假如有一个外部组件包Q不允许实现其接口,则就只能使用其动态代理了?br /> 2Q?直接讉K一个对象很困难Q或者说不能讉KQ此时只能是找个代理去访问,然后把结果反馈给自己?br /> 六?优缺?br /> 1) 优点: 向客L(fng)隐藏了访问某个对象的l节?qing)复杂性;可以动态地调用一个对象中的方法,且无需实现固定的接口?br /> 2) ~点Q暂时没发现
具体代码实现如下Q?br />
//定义不同的品之间的一定具备的标准Q用interface实现
//其中的method()Ҏ(gu)可看作提取出不同产品的共性,如手机都有类似的功能
interface IProductA{
public void method();
}
interface IProductB{
public void method();
}
//实现了品标准实现的一pd具体产品
//׃已经设计好A1由厂?生Q故以下输出代码?#8220;厂商x”
class ProductA1 implements IProductA{
public void method() {
System.out.println("厂商1 生ProductA1 ...");
}
}
class ProductA2 implements IProductA{
public void method() {
System.out.println("厂商2 生ProductA2 ...");
}
}
class ProductB1 implements IProductB{
public void method() {
System.out.println("厂商1 生ProductB1 ...");
}
}
class ProductB2 implements IProductB{
public void method() {
System.out.println("厂商2 生ProductB2 ...");
}
}
//每一U牌子的产品生工厂Q即不同的厂商负责自q子品的生
abstract class Factory1{
abstract IProductA getProductA1();
abstract IProductB getProductB1();
}
abstract class Factory2{
abstract IProductA getProductA2();
abstract IProductB getProductB2();
}
//具体的工厂用来生产相关的产品
class ConcreteFactory1 extends Factory1{
public IProductA getProductA1() {
return new ProductA1();
}
public IProductB getProductB1() {
return new ProductB1();
}
}
class ConcreteFactoryB extends Factory2{
public IProductA getProductA2() {
return new ProductA2();
}
public IProductB getProductB2() {
return new ProductB2();
}
}
//试c?
public class Client {
public static void main(String[] args) {
//厂商1负责生产品A1、B1
Factory1 factory1 = new ConcreteFactory1();
IProductA productA1 = factory1.getProductA1();
IProductB productB1 = factory1.getProductB1();
productA1.method();
productB1.method();
//厂商2负责生产品A2、B2
Factory2 factory2 = new ConcreteFactoryB();
IProductA productA2 = factory2.getProductA2();
IProductB productB2 = factory2.getProductB2();
productA2.method();
productB2.method();
}
}
q行l果Q?br />
厂商1 生ProductA1 ...
厂商1 生ProductB1 ...
厂商2 生ProductA2 ...
厂商2 生ProductB2 ...
其实Q上例中本来可以不用C两个抽象工厂Q只剩一个也可,但是q样子与我们现实生活中不同厂商各自生产自q产品有点不同Q所以就其分开Z个了Q也是两个厂商完全地分离开?br />
׃面类囑֏以知道,一旦Q何一个厂商开发出了品C的话Q那么这个厂商肯定需要扩展自q工厂来投入到C产品的生产中Q那么此时工?和工?需要进行一定的改变了,q很明显不符合开闭原则?/font>
大致意思是_(d)(x)使用一U比原有方式更简单的办法与系l交互。例如,我们拿一个文Ӟq个文g攑֜了第二抽屉里Q而第二个抽屉的钥匙放在了W一个抽屉里Q我们要惛_个文ӞW一步肯定要拿到W一个抽屉的钥匙Q然后打开它再拿出W二个抽屉的钥匙Q最后打开W二个抽屉取出文件?br /> 我就上面说的那个情Ş写一下实C?/font>
class DrawerOne {
public void open(){
System.out.println("W一个抽屉被打开?);
getKey();
}
public void getKey(){
System.out.println("得到W二个抽屉的钥匙");
}
}
class DrawerTwo{
public void open(){
System.out.println("W二个抽屉被打开?);
getFile();
}
public void getFile(){
System.out.println("得到q个重要文g");
}
}
public class Client{
public static void main(String []args){
DrawerOne darwerOne=new DrawerOne();
DrawerTwo darwerTwo=new DrawerTwo();
darwerOne.open();
darwerTwo.open();
}
}
׃没有使用Fa?ade模式Q可以看到要惛_到这个文件要首先打开W一个抽屉,然后再打开W二个抽屉,在我们实际所开发的pȝ中,有时候客戯实现某一操作Qƈ不需要知道实现这一操作的详l步骤,而是单地点击某一个按钮就可以得到自己惌的结果。下面对上面的代码用Fa?ade模式q行改进Q徏立一个FacadeDrawerc:(x)
class DrawerFacade{
DrawerOne darwerOne=new DrawerOne();
DrawerTwo darwerTwo=new DrawerTwo();
public void open(){
darwerOne.open();
darwerTwo.open();
}
}
修改Clientc:(x)
public class DrawerClient{
public static void main(String []args){
DrawerFacade drawer=new DrawerFacade();
drawer.open();
}
}
输出l果如下Q?/font>
W一个抽屉被打开?/font>
得到W二个抽屉的钥匙
W二个抽屉被打开?/font>
得到q个重要文g
正如上面所_(d)客户端clientQ它q不需要关心子pȝQ而是兛_DrawerFacade所留下来的和外部交互的接口Q而子pȝ在DrawerFacade的聚合?/font>
以上只是个h拙见Q哪里有不正的地方Q希望大家多多批评指正。^_^
Facade模式主要适用于以下几U情?
1)不需要用一个复杂系l的所有功能,而且可以创徏一个新的类Q包含访问系l的所有规则。如果只需要用系l的部分功能Q那么你为新cL创徏的API比原系l的API单的多?/font>
2)希望装或者隐藏系l原pȝ?/font>
3)希望使用原系l的功能Q而且q希望增加一些新的功能?/font>
4) ~写新类的成本小于所有h学会(x)使用或者未来维护原pȝ上所需的成本?/font>
q用׃n技术有效地支持大量l粒度对象?/font>
二、模式解?/font>
也就是说在一个系l中如果有多个相同的对象Q那么只׃n一份就可以了,不必每个都去实例化一个对象。在Flyweight模式中,׃要生各U各L(fng)对象Q所以在Flyweight(享元)模式中常出现Factory模式。Flyweight的内部状态是用来׃n?Flyweight factory负责l护一个对象存储池QF(tun)lyweight PoolQ来存放内部状态的对象。Flyweight模式是一个提高程序效率和性能的模??x)大大加快程序的q行速度?/font>
三、结构图
享元模式所涉及(qing)的角色有抽象享元角色、具体(单纯Qn元角艌Ӏ复合n元角艌Ӏn员工厂角Ԍ以及(qing)客户端角色等?/font>
抽象享元角色QF(tun)lyweightQ:(x)此角色是所有的具体享元cȝ类Qؓ(f)q些c规定出需要实现的公共接口或抽象类。那些需要外蕴状?External State)的操作可以通过Ҏ(gu)的参C入。抽象n元的接口使得享元变得可能Q但是ƈ不强制子cd行共享,因此q所有的享元对象都是可以׃n的?/font>
具体享元(ConcreteFlyweight)角色Q实现抽象n元角色所规定的接口。如果有内蕴状态的话,必须负责为内蕴状态提供存储空间。n元对象的内蕴状态必M对象所处的周围环境无关Q从而得n元对象可以在pȝ内共享。有时候具体n元角色又叫做单纯具体享元角色Q因为复合n元角色是由单U具体n元角色通过复合而成的?/font>
复合享元(UnsharableFlyweight)角色Q复合n元角色所代表的对象是不可以共享的Q但是一个复合n元对象可以分解成为多个本w是单纯享元对象的组合。复合n元角色又U做不可׃n的n元对象。这个角色一般很用?/font>
享元工厂(FlyweightFactoiy)角色Q本角色负责创徏和管理n元角艌Ӏ本角色必须保证享元对象可以被系l适当地共享。当一个客L(fng)对象h一个n元对象的时候,享元工厂角色需要检查系l中是否已经有一个符合要求的享元对象Q如果已l有了,享元工厂角色应当提供这个已有的享元对象Q如果系l中没有一个适当的n元对象的话,享元工厂角色应当创Z个新的合适的享元对象?/font>
客户?Client)角色Q本角色q需要自行存储所有n元对象的外蕴状态?/font>
四、一个例?/font>
import java.util.Hashtable;
/**
*
* @author <a href="mailto:flustar2008@163.com">flustar</a>
* @version 1.0
* Creation date: Jan 25, 2008 1:33:49 PM
*/
// "Flyweight"
abstract class Character
{
protected char symbol;
protected int width;
protected int height;
protected int ascent;
protected int descent;
protected int pointSize;
public abstract void Display(int pointSize);
}
// "ConcreteFlyweight"
class CharacterA extends Character
{
// Constructor
public CharacterA()
{
this.symbol = 'A';
this.height = 100;
this.width = 120;
this.ascent = 70;
this.descent = 0;
}
public void Display(int pointSize)
{
this.pointSize = pointSize;
System.out.println(this.symbol +
" (pointsize " + this.pointSize + ")");
}
}
// "ConcreteFlyweight"
class CharacterB extends Character
{
// Constructor
public CharacterB()
{
this.symbol = 'B';
this.height = 100;
this.width = 140;
this.ascent = 72;
this.descent = 0;
}
public void Display(int pointSize)
{
this.pointSize = pointSize;
System.out.println(this.symbol +
" (pointsize " + this.pointSize + ")");
}
}
// C, D, E, etc.
// "ConcreteFlyweight"
class CharacterZ extends Character
{
// Constructor
public CharacterZ()
{
this.symbol = 'Z';
this.height = 100;
this.width = 100;
this.ascent = 68;
this.descent = 0;
}
public void Display(int pointSize)
{
this.pointSize = pointSize;
System.out.println(this.symbol +
" (pointsize " + this.pointSize + ")");
}
}
// "FlyweightFactory"
class CharacterFactory
{
private Hashtable characters = new Hashtable();
public Character GetCharacter(char key)
{
// Uses "lazy initialization"
Character character = (Character)characters.get(key);
if (character == null)
{
switch (key)
{
case 'A': character = new CharacterA(); break;
case 'B': character = new CharacterB(); break;
//
case 'Z': character = new CharacterZ(); break;
}
characters.put(key, character);
}
return character;
}
}
// test application
public class Test
{
public static void main(String []args)
{
// Build a document with text
String document = "AAZZBBZB";
char[] chars = document.toCharArray();
CharacterFactory f = new CharacterFactory();
// extrinsic state
int pointSize = 10;
// For each character use a flyweight object
for(char c : chars)
{
pointSize++;
Character character = f.GetCharacter(c);
character.Display(pointSize);
}
}
}
五?适用?/p>
Flyweight模式的有效性很大程度上取决于如何用它以及(qing)在何处用它。当以下情况都成立时使用Flyweight模式?/p>
1Q一个应用程序用了大量的对象?/p>
2Q完全由于用大量的对象Q造成很大的存储开销?/p>
3Q对象的大多数状态都可变为外部状态?/p>
4Q如果删除对象的外部状态,那么可以用相对较?yu)的׃n对象取代很多l对象?/p>
5Q应用程序不依赖对象标识?/p>
六、优~点
1Qn元模式得系l更加复杂。ؓ(f)了对象可以׃nQ需要将一些状态外部化Q这使得E序的逻辑复杂化?
2Qn元模式将享元对象的状态外部化Q而读取外部状态得运行时间稍微变ѝ?br />
设计模式之Composite(l合)
Composite定义:
对象以?wi)Şl构l织h,以达?#8220;部分Q整?#8221; 的层ơ结构,使得客户端对单个对象和组合对象的使用h一致?
Composite比较Ҏ(gu)理解Q想到Composite应该想到树(wi)形结构图。组合体内这些对象都有共同接?当组合体一个对象的Ҏ(gu)被调用执行时QComposite遍?Iterator)整个?wi)Şl构,L同样包含q个Ҏ(gu)的对象ƈ实现调用执行。可以用牵一动百来Ş宏V?/p>
所以Composite模式使用到Iterator模式Q和Chain of Responsibility模式cM?/p>
Composite好处:
1.使客L(fng)调用单,客户端可以一致的使用l合l构或其中单个对象,用户׃必关p自己处理的是单个对象还是整个组合结构,q就化了客户端代码?br />
2.更容易在l合体内加入对象部g. 客户端不必因为加入了新的对象部g而更改代码?/p>
如何使用Composite?
首先定义一个接口或抽象c,q是设计模式通用方式了,其他设计模式Ҏ(gu)口内部定义限制不多,Composite却有个规定,那就是要在接口内部定义一个用于访问和理Compositel合体的对象们(或称部gComponentQ?
下面的代码是以抽象类定义Q一般尽量用接口interface,
public abstract class Equipment
{
private String name;
//|络h
public abstract double netPrice();
//折扣h
public abstract double discountPrice();
//增加部gҎ(gu)
public boolean add(Equipment equipment) { return false; }
//删除部gҎ(gu)
public boolean remove(Equipment equipment) { return false; }
//注意q里Q这里就提供一U用于访问组合体cȝ部gҎ(gu)?br />
public Iterator iter() { return null; }
public Equipment(final String name) { this.name=name; }
}
抽象cEquipment是Component定义Q代表着l合体类的对象们,Equipment中定义几个共同的Ҏ(gu)?/p>
public class Disk extends Equipment
{
public Disk(String name) { super(name); }
//定义Disk|络h?
public double netPrice() { return 1.; }
//定义了disk折扣h?.5 Ҏ(gu)?br />
public double discountPrice() { return .5; }
}
Disk是组合体内的一个对象,或称一个部Ӟq个部g是个单独元素(Primitive)?br />
q有一U可能是Q一个部件也是一个组合体Q就是说q个部g下面q有'儿子'Q这是树(wi)形结构中通常的情况,应该比较Ҏ(gu)理解。现在我们先要定义这个组合体Q?/p>
abstract class CompositeEquipment extends Equipment
{
private int i=0;
//定义一个Vector 用来存放'儿子'
private Lsit equipment=new ArrayList();
public CompositeEquipment(String name) { super(name); }
public boolean add(Equipment equipment) {
this.equipment.add(equipment);
return true;
}
public double netPrice()
{
double netPrice=0.;
Iterator iter=equipment.iterator();
for(iter.hasNext())
netPrice+=((Equipment)iter.next()).netPrice();
return netPrice;
}
public double discountPrice()
{
double discountPrice=0.;
Iterator iter=equipment.iterator();
for(iter.hasNext())
discountPrice+=((Equipment)iter.next()).discountPrice();
return discountPrice;
}
//注意q里Q这里就提供用于讉K自己l合体内的部件方法?br />
//上面dIsk 之所以没有,是因为Disk是个单独(Primitive)的元?
public Iterator iter()
{
return equipment.iterator() ;
{
//重蝲IteratorҎ(gu)
public boolean hasNext() { return i<equipment.size(); }
//重蝲IteratorҎ(gu)
public Object next()
{
if(hasNext())
return equipment.elementAt(i++);
else
throw new NoSuchElementException();
}
}
上面CompositeEquipmentl承了Equipment,同时己里面的对象们提供了外部讉K的方?重蝲了Iterator,Iterator是Java的Collection的一个接口,是Iterator模式的实?
我们再看看CompositeEquipment的两个具体类:盘盒Chassis和箱子CabinetQ箱子里面可以放很多东西Q如底板Q电(sh)源盒Q硬盘盒{;盘盒里面可以放一些小讑֤Q如盘 软驱{。无疑这两个都是属于l合体性质的?/p>
public class Chassis extends CompositeEquipment
{
public Chassis(String name) { super(name); }
public double netPrice() { return 1.+super.netPrice(); }
public double discountPrice() { return .5+super.discountPrice(); }
}
public class Cabinet extends CompositeEquipment
{
public Cabinet(String name) { super(name); }
public double netPrice() { return 1.+super.netPrice(); }
public double discountPrice() { return .5+super.discountPrice(); }
}
x我们完成了整个Composite模式的架构?/p>
我们可以看看客户端调用Composote代码:
Cabinet cabinet=new Cabinet("Tower");
Chassis chassis=new Chassis("PC Chassis");
//PC Chassis装到Tower?(盘盒装到箱子里)
cabinet.add(chassis);
//一?0GB的硬盘装?PC Chassis (硬盘装到盘盒里)
chassis.add(new Disk("10 GB"));
//调用 netPrice()Ҏ(gu);
System.out.println("netPrice="+cabinet.netPrice());
System.out.println("discountPrice="+cabinet.discountPrice());
上面调用的方法netPrice()或discountPrice()Q实际上Composite使用Iterator遍历了整个树(wi)形结?L同样包含q个Ҏ(gu)的对象ƈ实现调用执行.
Composite是个很y妙体现智慧的模式Q在实际应用中,如果到?wi)Şl构Q我们就可以试是否可以使用q个模式?/p>
以论坛ؓ(f)例,一个版(forum)中有很多帖子(message),q些帖子有原始脓(chung)Q有对原始脓(chung)的回应脓(chung)Q是个典型的?wi)Şl构Q那么当然可以用Composite模式Q那么我们进入Jive中看看,是如何实现的.
Jive解剖
在Jive?ForumThread是ForumMessages的容器container(l合?.也就是说QForumThreadcM我们上例中的 CompositeEquipment.它和messages的关pd图:(x)
[thread]
|- [message]
|- [message]
|- [message]
|- [message]
|- [message]
我们在ForumThread看到如下代码Q?/p>
public interface ForumThread {
....
public void addMessage(ForumMessage parentMessage, ForumMessage newMessage)
throws UnauthorizedException;
public void deleteMessage(ForumMessage message)
throws UnauthorizedException;
public Iterator messages();
....
}
cMCompositeEquipment, 提供用于讉K自己l合体内的部件方? 增加 删除 遍历.
? 试E序与源E序的组l结构,“分离但等?#8221;原则
? junit.jar加入到classpath中,可以被javacd扑ֈQ但不能j(lu)avad识别?/p>
junit与ant构徏工具的集?/p>
如果junitd下面没有讄<formatter>子元素,那将无法打印l的信息?br /> 一个junitd下可以有多个<formatter>子元?<formatter>的usefile属性决定是否生成文件?br /> junitreportd首先把多个test-....xml文g合ƈ成一个汇ȝxml文gQ其todir属性指定该文g的存储目?其report子元素用于指定生成的html文g的目录。在用junitreport之前Q要心junit的haltonfaiure?/p>
batchtest指定的java源文件或class文gQ而不是要q行的测试类名称Qjunit从这些文件中扑և要运行的试cd?/p>
? junit与ant构徏工具的集成的例子,
<project name="junitlesson" default="run">
<property name="src.java.dir" value="src/java"/><!--被测试的java源文件的路径-->
<property name="src.test.dir" value="src/test"/><!--试的java源文件的路径-->
<property name="classes.java.dir" value="classes/java"/><!--被测试的class文g的\?->
<property name="classes.test.dir" value="classes/test"/><!--试的class文g的\?->
<target name="init">
<mkdir dir="${classes.java.dir}"/>
<mkdir dir="${classes.test.dir}"/>
</target>
<target name="compileJava" depends="init">
<javac srcdir="${src.java.dir}/cn/itcast" destdir="${classes.java.dir}"/>
</target>
<target name="compileTest" depends="init">
<javac srcdir="${src.test.dir}/cn/itcast" destdir="${classes.test.dir}">
<classpath>
<pathelement path="."/>
<pathelement path="${classes.java.dir}"/>
</classpath>
</javac>
</target>
<target name="compile" depends="compileJava,compileTest"/>
<target name="run" depends="compile">
<mkdir dir="testResult"/>
<junit fork="true" printsummary="on"><!-- haltonfailure="true"--><!--注意:q句一加上ȝ话如果程序里面出错了后面的junitreport׃执行?->
<classpath>
<pathelement path="${classes.test.dir}"/>
<pathelement path="${classes.java.dir}"/>
</classpath>
<test name="cn.itcast.TestCalculator" todir=".\testResult" outfile="result"/>
<formatter type="xml" />
</junit>
<junitreport todir=".\testResult">
<fileset dir=".\testResult">
<include name="result.xml"/>
</fileset>
<report format="frames" todir=".\testResult"/>
</junitreport>
</target>
</project>
<!--正确执行的话打开src所在的目录׃(x)看到一个新建的testResult目录,里面已经生成了一大堆试报告的网?:) -->
此系l中的三个部分是气象站(获取实际气象数据的物理装|)、WeatherData对象Q追t来自气象站的数据,q更新布告板Q和布告板(昄目前天气状况l用L(fng)Q?/font>
1、定义观察者模?/font>
观察者模式定义了对象之间的一对多依赖Q这样一来,当一个对象改变状态时Q它的所有依赖者都?x)收到通知q自动更新?/font>
2.设计气象?/font>
1Q定义接?/p>
Q?QSubject接口
Subject.java
package com.leanhuadeng.DesignPattern.Observer;
public interface Subject {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
}
Q?QObserver接口
Observer.java
package com.leanhuadeng.DesignPattern.Observer;
public interface Observer {
public void update(float temp,float humidity,float pressure);
}
Q?QDisplayment接口
Displayment.java
package com.leanhuadeng.DesignPattern.Observer;
public interface Displayment {
public void display();
}
2Q实现接?/p>
Q?QWeatherData
WeatherData.java
package com.leanhuadeng.DesignPattern.Observer;
import java.util.ArrayList;
public class WeatherData implements Subject {
private ArrayList observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData(){
observers=new ArrayList();
}
public void notifyObservers() {
for(int i=0;i<observers.size();i++){
Observer observer=(Observer)observers.get(i);
observer.update(temperature, humidity, pressure);
}
}
public void registerObserver(Observer o) {
observers.add(o);
}
public void removeObserver(Observer o) {
int i=observers.indexOf(o);
if(i>=0){
observers.remove(i);
}
}
public void measurementsChanged(){
notifyObservers();
}
public void setMeasurements(float temperature,float humidity,float pressure){
this.temperature=temperature;
this.humidity=humidity;
this.pressure=pressure;
measurementsChanged();
}
}
Q?QCurrentConditionsDisplay
CurrentConditionsDisplay.java
package com.leanhuadeng.DesignPattern.Observer;
public class CurrentConditionsDisplay implements Observer, Displayment {
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentConditionsDisplay(Subject weatherData){
this.weatherData=weatherData;
weatherData.registerObserver(this);
}
public void update(float temp, float humidity, float pressure) {
this.temperature=temp;
this.humidity=humidity;
display();
}
public void display() {
System.out.println("Current conditions:"+temperature+"F degrees and "+humidity+"% humidity");
}
}
Q?QStatisticsDisplay
StatisticsDisplay.java
package com.leanhuadeng.DesignPattern.Observer;
import java.util.*;
public class StatisticsDisplay implements Observer, Displayment {
private float maxTemp = 0.0f;
private float minTemp = 200;
private float tempSum= 0.0f;
private int numReadings;
private WeatherData weatherData;
public StatisticsDisplay(WeatherData weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
public void update(float temp, float humidity, float pressure) {
tempSum += temp;
numReadings++;
if (temp > maxTemp) {
maxTemp = temp;
}
if (temp < minTemp) {
minTemp = temp;
}
display();
}
public void display() {
System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings)+ "/" + maxTemp + "/" + minTemp);
}
}
Q?QForecastDisplay
ForecastDisplay.java
package com.leanhuadeng.DesignPattern.Observer;
import java.util.*;
public class ForecastDisplay implements Observer, Displayment {
private float currentPressure = 29.92f;
private float lastPressure;
private WeatherData weatherData;
public ForecastDisplay(WeatherData weatherData) {
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
public void update(float temp, float humidity, float pressure) {
lastPressure = currentPressure;
currentPressure = pressure;
display();
}
public void display() {
System.out.print("Forecast: ");
if (currentPressure > lastPressure) {
System.out.println("Improving weather on the way!");
} else if (currentPressure == lastPressure) {
System.out.println("More of the same");
} else if (currentPressure < lastPressure) {
System.out.println("Watch out for cooler, rainy weather");
}
}
}
3Q实现气象站
WeatherStation.java
package com.leanhuadeng.DesignPattern.Observer;
public class WeatherStation {
public static void main(String[] args){
WeatherData weatherData=new WeatherData();
CurrentConditionsDisplay currentDisplay=new CurrentConditionsDisplay(weatherData);
StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);
weatherData.setMeasurements(80, 65, 30.4f);
weatherData.setMeasurements(82, 70, 29.2f);
weatherData.setMeasurements(78, 90, 29.2f);
}
}
星巴兹(StarbuzzQ是以扩张速度最快而闻名的咖啡q锁店。如果你在街角看到它的店Q在寚w街上肯定q会(x)看到另一家。因为扩张速度实在太快了,他们准备更新订单pȝQ以合乎他们的饮料供应要求。他们原先的c设计是q样?#8230;…
购买咖啡Ӟ也可以要求在其中加入各种调料Q例如:(x)蒸奶Q?/span>Steamed MilkQ、豆(SoyQ、摩卡(MochaQ也是巧克力风呻I或覆盖奶泡。星巴兹?x)根据所加入的调料收取不同的费用。所以订单系l必考虑到这些调料部分?/span>
二、Decorator模式1、一个原?/font>
cd该对扩展开放,对修改关?/font>
2、定义装饰者模?/font>
装饰者模式动态地责任附加到对象上。若要扩展功能,装饰者提供了比承更有弹性的替代Ҏ(gu)?br />
三、代码实?/font>
1Q定义抽象类
Q?Q饮料抽象类Beverage
Beverage.java
package com.leanhuadeng.DesignPattern.Decorator;
/*
* Beverage是一个抽象类,有两个方?br />
*/
public abstract class Beverage {
public String description="Unknown Beverage";
/*
* getDescription()已经在此实现?但是cost()必须在子cM实现
*/
public String getDescription() {
return description;
}
public abstract double cost();
}
三、代码实?br />
(2)调料抽象cCondimentDecorator
CondimentDecorator.java
package com.leanhuadeng.DesignPattern.Decorator;
/*
* 首先,必须让CondimentDecorator能够取代Beverage,所以将CondimentDecorator扩展自Beveragec?br />
*/
public abstract class CondimentDecorator extends Beverage {
//所有的调料装饰者都必须重新实现getDescription()Ҏ(gu).
public abstract String getDescription();
}
2Q饮料实?/font>
(1)Espresso
Espresso.java
package com.leanhuadeng.DesignPattern.Decorator;
/**//*
* 首先,必须让CondimentDecorator能够取代Beverage,所以将CondimentDecorator扩展自Beveragec?br />
*/
public abstract class CondimentDecorator extends Beverage {
//所有的调料装饰者都必须重新实现getDescription()Ҏ(gu).
public abstract String getDescription();
}
(2)HouseBlend
HouseBlend.java
package com.leanhuadeng.DesignPattern.Decorator.drink;
import com.leanhuadeng.DesignPattern.Decorator.Beverage;
public class HouseBlend extends Beverage {
public HouseBlend() {
description="House Blend Coffee";
}
@Override
public double cost() {
return 0.89;
}
}
(3)DarkRoast
DarkRoast.java
package com.leanhuadeng.DesignPattern.Decorator.drink;
import com.leanhuadeng.DesignPattern.Decorator.Beverage;
public class DarkRoast extends Beverage {
public DarkRoast() {
description="Dark Roast Coffee";
}
@Override
public double cost() {
return 0.99;
}
}
(4)Decaf
Decaf.java
package com.leanhuadeng.DesignPattern.Decorator.drink;
import com.leanhuadeng.DesignPattern.Decorator.Beverage;
public class Decaf extends Beverage {
public Decaf() {
description="Decaf Coffee";
}
@Override
public double cost() {
return 1.05;
}
}
3Q调料实?/font>
(1)Mocha
Mocha.java
package com.leanhuadeng.DesignPattern.Decorator.condiment;
import com.leanhuadeng.DesignPattern.Decorator.Beverage;
import com.leanhuadeng.DesignPattern.Decorator.CondimentDecorator;
public class Mocha extends CondimentDecorator {
/*
* 要让Mocha能够引用一个Beverage,做法如下:一是用一个实例变量记录饮?也就是被装饰?
* 二是惛_法让装饰?饮料)记录到实例变量中,x饮料当作构造器的参?再由构造器此饮料记录在实例变量中
*/
Beverage beverage;
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
/*
* 我们希望叙述不只是描q饮?而是完整的连调料都描q出?br />
*/
return beverage.getDescription()+",Mocha";
}
@Override
public double cost() {
/*
* 要计带Mocha饮料的h(hun)?首先把调用委托给装饰对象,以计h(hun)?然后再加上Mocha的h(hun)?得到最后结?br />
*/
return 0.20+beverage.cost();
}
}
(2)Soy
Soy.java
package com.leanhuadeng.DesignPattern.Decorator.condiment;
import com.leanhuadeng.DesignPattern.Decorator.Beverage;
import com.leanhuadeng.DesignPattern.Decorator.CondimentDecorator;
public class Soy extends CondimentDecorator {
Beverage beverage;
public Soy(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ", Soy";
}
public double cost() {
return .15 + beverage.cost();
}
}
(3)Whip
Whip.java
package com.leanhuadeng.DesignPattern.Decorator.condiment;
import com.leanhuadeng.DesignPattern.Decorator.Beverage;
import com.leanhuadeng.DesignPattern.Decorator.CondimentDecorator;
public class Whip extends CondimentDecorator {
Beverage beverage;
public Whip(Beverage beverage) {
this.beverage = beverage;
}
public String getDescription() {
return beverage.getDescription() + ", Whip";
}
public double cost() {
return .10 + beverage.cost();
}
}
4Q测试类StarbuzzCoffee
StarbuzzCoffee.java
package com.leanhuadeng.DesignPattern.Decorator;
import com.leanhuadeng.DesignPattern.Decorator.condiment.Mocha;
import com.leanhuadeng.DesignPattern.Decorator.condiment.Soy;
import com.leanhuadeng.DesignPattern.Decorator.condiment.Whip;
import com.leanhuadeng.DesignPattern.Decorator.drink.DarkRoast;
import com.leanhuadeng.DesignPattern.Decorator.drink.Espresso;
import com.leanhuadeng.DesignPattern.Decorator.drink.HouseBlend;
public class StarbuzzCoffee {
public static void main(String args[]){
/*
* 订一杯Espresso,不需要调?打印出它的描q和价钱.
*/
Beverage beverage=new Espresso();
System.out.println(beverage.getDescription()+" $"+beverage.cost());
/*
* 刉一个DarkRoast对象,用Mocha,Whip装饰?br />
*/
Beverage beverage2=new DarkRoast();
beverage2=new Mocha(beverage2);
beverage2=new Mocha(beverage2);
beverage2=new Whip(beverage2);
System.out.println(beverage2.getDescription()+" $"+beverage2.cost());
/*
* 最?再来一杯调料ؓ(f)豆浆,摩卡\奶的HouseBlend咖啡
*/
Beverage beverage3=new HouseBlend();
beverage3=new Soy(beverage3);
beverage3=new Mocha(beverage3);
beverage3=new Whip(beverage3);
System.out.println(beverage3.getDescription()+" $"+beverage3.cost());
}
}