Java23种设计模式
来源:互联网 发布:手机流量打电话软件 编辑:程序博客网 时间:2024/06/11 03:05
一、设计模式入门:
1.设计模式是人们在面对同类型软件工程设计问题所总结出的一些有用经验。模式不是代码,而是某类问题的通用设计解决方案
2.设计模式的本质目的是使软件工程在维护性、扩展性、变化性、复杂度方面成O(N)
3.OOP是原则,设计模式是具体方法、工具
————————————————————————————————————————————
二、策略模式
从文字方面可能我们很难理解,所以我们从实际项目入手
现在假设我们有个“鸭子项目”,首先我们用OOP的角度设计这个项目,找到鸭子中共同的特性抽取在父类中并具体实现,不同的特性不实现,由子类具体实现,好下面看代码:
public abstract class Duck { /** * 叫声和游泳为相同的特性抽取并具体实现 */ public void Quack() { System.out.println("~~gaga~~"); } public void swim() { System.out.println("~~im swim~~"); } /** * 外貌为不同的特性设计为抽象的方法,有子类具体实现 */ public abstract void display();}
现在我们看它的子类:
public class GreenHeadDuck extends Duck { @Override public void display() { System.out.println("**GreenHead**"); }}
public class RedHeadDuck extends Duck { @Override public void display() { System.out.println("**RedHead**"); }}
好的,现在我们可以看到使用OOP可以很好的解决目前的问题,但是我们往往是需求不断,所以我们现在又来一个新需求:添加会飞的鸭子
好办啊,我们只要在父类中添加一个新的方法:
public abstract class Duck { /** * 叫声和游泳为相同的特性抽取并具体实现 */ public void Quack() { System.out.println("~~gaga~~"); } public void swim() { System.out.println("~~im swim~~"); } /**针对新需求的方法*/ public void Fly() { System.out.println("~~im fly~~"); } /** * 外貌为不同的特性设计为抽象的方法,有子类具体实现 */ public abstract void display();}
问题来了,这个Fly让所有子类都会飞了,这是不科学的。
继承的问题:对类的局部改动,尤其超类的局部改动,会影响其他部分。影响会有溢出效应
好现在我们继续用OOP的方式去解决,使其子类覆盖Fly:
public class GreenHeadDuck extends Duck { @Override public void display() { System.out.println("**GreenHead**"); } /** * 覆盖 * */ public void Fly() { System.out.println("~~no fly~~"); }}
可以看到貌似问题是解决了,我们现在的鸭子类只有2种,如果有上百种呢,那不是都要去覆盖。所以这不是一种好的设计模式
分析问题:
需要新的设计方式,应对项目的扩展性,降低复杂度:
1)分析项目变化与不变部分,提取变化部分,抽象成接口+实现;
2)鸭子哪些功能是会根据新需求变化的?叫声、飞行…
我们将变化的功能设计成接口,下面看代码:
public interface FlyBehavior { void fly();}
public interface QuackBehavior { void quack();}
来看看新的Duck类:
public abstract class Duck { /** * 父类定义行为出来,但是没有具体实例化 */ FlyBehavior mFlyBehavior; QuackBehavior mQuackBehavior; public Duck() { } public void Fly() { if (mFlyBehavior!=null) { mFlyBehavior.fly(); } } public void Quack() { if (mQuackBehavior!=null) { mQuackBehavior.quack(); } } /** * 子类可以透过两个行为的set方法去动态改变自己的具体行为 */ public void setmFlyBehavior(FlyBehavior mFlyBehavior) { this.mFlyBehavior = mFlyBehavior; } public void setmQuackBehavior(QuackBehavior mQuackBehavior) { this.mQuackBehavior = mQuackBehavior; } public abstract void display();}
然后在去看看子类:
public class RedHeadDuck extends Duck{ public RedHeadDuck() { mFlyBehavior=new GoodFlyBehavior(); mQuackBehavior=new GaGaQuackBehavior(); } @Override public void display() { System.out.println("redDuck"); }}
public class GreenHeadDuck extends Duck{ public GreenHeadDuck() { mFlyBehavior=new BadFlyBehavior(); mQuackBehavior=new GeGeQuackBehavior(); } @Override public void display() { System.out.println("greenDuck"); }}
再来看看接口实现类:
public class BadFlyBehavior implements FlyBehavior{ @Override public void fly() { System.out.println("bad fly"); }}
public class GoodFlyBehavior implements FlyBehavior{ @Override public void fly() { System.out.println("good fly"); }}
public class NoFlyBehavior implements FlyBehavior{ @Override public void fly() { System.out.println("No fly"); }}
public class GaGaQuackBehavior implements QuackBehavior{ @Override public void quack() { System.out.println("gaga quack"); }}
public class GeGeQuackBehavior implements QuackBehavior{ @Override public void quack() { System.out.println("gege quack"); }}
public class NoQuackBehavior implements QuackBehavior{ @Override public void quack() { System.out.println("No Quack"); }}
好的,现在我们来分析一下这个设计,在父类中我们定义好FlyBehavior & QuackBehavior 两个行为接口,然后在子类构造方法中分别设定对应的具体行为
现在来测试一下:
策略模式:分别封装行为接口,实现算法族,超类里放行为接口对象,在子类里具体设定行为对象。原则就是:分离变化部分,封装接口,基于接口编程各种功能。此模式让行为算法的变化独立于算法的使用者
—————————————————————————————————————————————
三、观察者模式:
跟之前一样,我们还是通过实际项目入手,之后,这句话我就不重复了,直接从项目开始讲解了
现在假设我们有一个需要为A公司实时提供天气的天气预报接口项目,好的,首先我们还是以OOP的方式去解决问题,首先我们创建一个天气台对象并提供相关方法假设它可以实时为A公司提供天气数据,下面看代码:
public class MeteorologicalStation { private float pressure; private float temperature; private float humidity; private ACompany company; public MeteorologicalStation(ACompany company) { this.company=company; } public float getPressure() { return pressure; } public float getTemperature() { return temperature; } public float getHumidity() { return humidity; } /** * 实时提供天气情况 * */ public void uploadData(float pressure,float temperature,float humidity){ company.getMeteorologicalStationData(pressure, temperature, humidity); }}
ACompany为A公司:
public class ACompany { public void getMeteorologicalStationData(float pressure, float temperature, float humidity) { System.out.println("pressure: "+pressure+",temperature: "+temperature+",humidity: "+humidity); }}
测试
好的,到了这里我们从OOP角度已经初步解决了这个项目,假设现在B公司也需要实时获取天气台提供的天气情况,如果我们还是继续使用OOP去设计这个项目,需要在天气台接口中添加BCompany,并在实时更新的函数中调用BCompany的获取天气的函数,这貌似可以解决这个需求,但是加入后续加入C,D,E..公司,那么天气台接口修改的代码也是比较大,但是我们知道在通常实际开发中,接口一般不会随着他人的接入而更改,所以现在我们使用观察者模式去设计这个项目:
/** * 该接口相当于天气台管理者,谁想接入我和离开我都必须通过它去管理 * */public interface Subject { void registerObserver(Observer o); void removeObserver(Observer o); void notifObserver();}
/** * 通过该接口,每个想要接入的公司实现该方法即可。 */public interface Observer { void getMeteorologicalStationData(float pressure,float temperature,float humidity);}
public class MeteorologicalStation implements Subject { private float pressure; private float temperature; private float humidity; private ArrayList<Observer> observers; public MeteorologicalStation(ACompany company) { observers = new ArrayList<Observer>(); } public float getPressure() { return pressure; } public float getTemperature() { return temperature; } public float getHumidity() { return humidity; } public void uploadData(float pressure, float temperature, float humidity) { this.pressure = pressure; this.temperature = temperature; this.humidity = humidity; notifObserver(); } @Override public void registerObserver(Observer o) { if (!observers.contains(o)) { observers.add(o); } } @Override public void removeObserver(Observer o) { if (observers.contains(o)) { observers.remove(o); } } @Override public void notifObserver() { for (int i = 0; i < observers.size(); i++) { Observer observer = observers.get(i); observer.getMeteorologicalStationData(getPressure(), getTemperature(), getHumidity()); } }}
现在我们来分析天气台类,天气台实现了Subject,并用集合的形式方便的管理多个客户的接入与离开,在notifObserver中实时的上传天气数据。好的,现在我们看看其他客户的接入:
public class ACompany implements Observer{ @Override public void getMeteorologicalStationData(float pressure,float temperature,float humidity){ System.out.println("A pressure: "+pressure+",temperature: "+temperature+",humidity: "+humidity); }}
public class BCompany implements Observer{ @Override public void getMeteorologicalStationData(float pressure, float temperature, float humidity) { System.out.println("B pressure: "+pressure+",temperature: "+temperature+",humidity: "+humidity); }}
好的我们可以看到无论有多少个公司想要接入,现在只需要实现Observer接口即可。现在我们来测试下:
看到这里我们已经知道了观察者模式的好处,下面我们看看java内置的观察者:
public class MeteorologicalStation extends Observable { private float pressure; private float temperature; private float humidity; public float getPressure() { return pressure; } public float getTemperature() { return temperature; } public float getHumidity() { return humidity; } public void uploadData(float pressure, float temperature, float humidity) { this.pressure = pressure; this.temperature = temperature; this.humidity = humidity; WeatherData data=new WeatherData(pressure, temperature, humidity); /** * setChanged * 根据有些特定的需求出现的,设置后唤醒才有效, * 例如我们不需要温度改变0.5我们也去唤醒客户, * 因此这里我们可以判断后在设置 * */ this.setChanged(); this.notifyObservers(data); } public class WeatherData{ public float pressure; public float temperature; public float humidity; public WeatherData(float pressure, float temperature, float humidity) { this.pressure = pressure; this.temperature = temperature; this.humidity = humidity; } }}
从代码中可以看到Observable 是一个类 而不是像我们一样的Subject的接口,
this.setChanged();this.notifyObservers(data);
两个一起使用才有效,下面我们看看A,B公司需要怎么做:
public class ACompany implements Observer{ @Override public void update(Observable arg0, Object arg1) { WeatherData data=(WeatherData) arg1; System.out.println("A pressure: "+data.pressure+",temperature: "+data.temperature+",humidity: "+data.humidity); }}
public class BCompany implements Observer{ @Override public void update(Observable o, Object arg) { WeatherData data=(WeatherData) arg; System.out.println("B pressure: "+data.pressure+",temperature: "+data.temperature+",humidity: "+data.humidity); }}
A,B公司只要实现系统的Observer接口,和我们刚才的设计是一样的。最后我们在测试一下:
可以看到和我们之前的效果完全一样
—————————————————————————————————————————————
四、装饰者模式
咖啡馆订单系统项目:
咖啡种类:Espresso、ShortBlack、LongBlack、Decaf
调料:Milk、Soy、Chocolate
顾客首先选择咖啡种类,然后在选择是否需要调料,我们需要描述出顾客选择的咖啡种类及其调料名称,及其花费的总价。
好的,现在我们来看一个差的方案:
首先是定义了一个抽象的Drink父类,里面定义出一个抽象的cost()方法用于返回消费的总价,用setDescription去描述各个消费商品的名称,使用getDescrption将客户的消费订单打印,后面又将大概的客户消费可能出现的情况一一去列举出来,但是 由于这个消费情况太多,所以没有用代码去描述,各个消费情况继承Drink类,实现cost()抽象方法,使用父类的super.getPrice()返回总价,在构造中使用super.setDescriptin()和super.setPrice()分别去设置自己的产品名称及其价格。图中我们可以看到加入有许多种情况,我们需要定义太多的类。
好的,我们在来看一个好一点的方案:
从图中可以看到基本和第一种方案基本类似,只是将调料的种类在父类中体现,如果顾客需要在咖啡中加入调料调用setXXX方法,使用hasXXX去判断是否有某中调料,但是就像图中所示,会产生如果增加调料种类,需要去更改父类的方法,并且此方式无法将同分调料增加多份
好的,我们现在使用装饰者来看看:
public abstract class Drink { private int price; private String description; public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } public String getDescription() { return description+"--"+this.getPrice(); } public void setDescription(String description) { this.description = description; } public abstract int cost();}
public class Coffer extends Drink{ @Override public int cost() { return super.getPrice(); }}
public class Decaf extends Coffer{ public Decaf() { super.setDescription("脱因咖啡"); super.setPrice(10); }}
public class Espresso extends Coffer{ public Espresso() { super.setDescription("浓咖啡"); super.setPrice(11); }}
public class LongBlack extends Coffer{ public LongBlack() { super.setDescription("热咖啡"); super.setPrice(12); }}
public class ShortBlack extends Coffer{ public ShortBlack() { super.setDescription("小杯黑咖啡"); super.setPrice(13); }}
从上面可以看出我们首先定义出一个抽象的Drink父类,用于设置和得到各个咖啡的描述及其价格,然后定义出一个Coffer类继承自Drink类,重写cost()方法,用于返回每次消费的价格,各个咖啡类需要继承Coffer类在构造函中设置各自的描述及其价格,接下来我们看看装饰者代码:
public class Decorator extends Drink{ Drink drink; public Decorator(Drink drink) { this.drink=drink; } @Override public int cost() { return super.getPrice()+this.drink.cost(); } @Override public String getDescription() { return super.getDescription()+"&&"+this.drink.getDescription(); }}
可以看到装饰者代码我们需要继承自Drink类,构造函数中带入其作为参数,重写cost与getDescription方法,使用迭代的形式将每次添加的调料累加,形成一个循环,将其返回,:
public class Chocolate extends Decorator{ public Chocolate(Drink drink) { super(drink); super.setDescription("巧克力"); super.setPrice(2); }}
public class Milk extends Decorator{ public Milk(Drink drink) { super(drink); super.setDescription("牛奶"); super.setPrice(2); }}
public class Soy extends Decorator{ public Soy(Drink drink) { super(drink); super.setDescription("大豆"); super.setPrice(4); }}
各个调料类需要继承自Decorator 装饰者类,然后在构造函数中带入Drink类作为参数,传给父类,并需要设置其价格及其描述,
好的现在我们来测试下效果:
public class main { public static void main(String[] args) { LongBlack longBlack=new LongBlack(); System.out.println("order1 price: "+longBlack.cost()); System.out.println("order1 desc: "+longBlack.getDescription()); System.out.println("---------------"); Drink order; order=new Espresso(); order=new Milk(order); order=new Chocolate(order); order=new Chocolate(order); System.out.println("order2 price: "+order.cost()); System.out.println("order2 desc: "+order.getDescription()); }}
从上面的代码中我们可以看出使用装饰者后,我们无论需要多少调料都是可以累加的,加入有一天有一个新的咖啡种类或者调料,我们只需要按照上面的类似就行了,无需去改动父类和增加许多类。
好的,现在我们来看看java内置的装饰者模式:
从图中可以看出java 中IO流的设计就是我们其中的装饰者模式,那好现在我们也给自己添加一个大写额输入流类:
public class UpperCaseInputStream extends FilterInputStream{ protected UpperCaseInputStream(InputStream in) { super(in); } @Override public int read() throws IOException { int c = super.read(); return c==-1?c:Character.toUpperCase((char)(c)); } @Override public int read(byte[] b, int off, int len) throws IOException { int size=super.read(b, off, len); for(int i=0;i<size;i++){ b[i]=(byte) Character.toUpperCase((char)(size)); } return size; }}
从代码中可以看到很简单的继承FilterInputStream,然后在read方法中将其转换为大写即可,这种装饰者的设计使我们可以很简单的去添加一个子类,我们在来测试一下:
public class main { public static void main(String[] args) { InputStream is=null; int c=0; try { is=new FileInputStream("C:\\Users\\Administrator\\Desktop\\test.txt"); is =new BufferedInputStream(is); is = new UpperCaseInputStream(is); while((c=is.read())>0){ System.out.print((char)c); } } catch (IOException e) { e.printStackTrace(); } }}
装饰着模式是一种开放-关闭原则的设计意义,开放即为以后设计添加子类方便,关闭即父类不用更改
—————————————————————————————————————————————
五、单例模式
巧克力工厂项目,工厂只有一个锅炉,往锅炉中添加佐料,如果满了则其他人不能再次添加,或者必须倾倒后添加,然后将锅炉烧开,如果锅炉已经烧开就无需再次烧开,或者必须倾倒后再次烧开
好的,首先我们还是用普通的方式去做:
public class ChocolateFactor { private boolean isEmpty; private boolean isBoiled; public ChocolateFactor() { isEmpty=true; isBoiled=false; } public void fill(){ if (isEmpty) { isEmpty=false; isBoiled=false; System.out.println("锅炉满了"); } } public void boil(){ if ((!isEmpty)&&(!isBoiled)) { isBoiled=true; System.out.println("锅炉已经沸腾了"); } } public void dump(){ if ((!isEmpty)&&(isBoiled)) { isEmpty=true; isBoiled=false; System.out.println("锅炉啥都没有咯"); } }}
然后我们测试下:
public class test { public static void main(String[] args) { ChocolateFactor factor=new ChocolateFactor(); factor.fill(); factor.boil(); ChocolateFactor factor1=new ChocolateFactor(); factor1.fill(); factor1.boil(); }}
可以看到即使锅炉满了和沸腾了,其他人仍然可以往锅炉中添加,所以这是不合常理的,现在我们来用单例试试:
public class ChocolateFactor { private boolean isEmpty; private boolean isBoiled; private static ChocolateFactor instance=null; private ChocolateFactor() { isEmpty=true; isBoiled=false; } public static ChocolateFactor getInstance(){ if (instance==null) { instance=new ChocolateFactor(); } return instance; } public void fill(){ if (isEmpty) { isEmpty=false; isBoiled=false; System.out.println("锅炉满了"); } } public void boil(){ if ((!isEmpty)&&(!isBoiled)) { isBoiled=true; System.out.println("锅炉已经沸腾了"); } } public void dump(){ if ((!isEmpty)&&(isBoiled)) { isEmpty=true; isBoiled=false; System.out.println("锅炉啥都没有咯"); } }}
代码中可以看到我们将构造方法私有化,提供一个静态的方法去返回巧克力工厂实例,将巧克力工厂静态化,使其在内存中只保留一份。现在我们来测试下:
public class test { public static void main(String[] args) { ChocolateFactor factor=ChocolateFactor.getInstance(); factor.fill(); factor.boil(); ChocolateFactor factor1=ChocolateFactor.getInstance(); factor1.fill(); factor1.boil(); }}
可以看到锅炉满了和沸腾之后对其再次操作是无效的。
但是其实这也是存在问题的,现在假设我们在多线程的状态,线程一走到了if (instance==null) { 后cpu不让它工作了,开始线程二工作,线程二将整个getInstance走完了,现在cpu又让线程1执行,这时候,线程1又会去new 一个新的巧克力工厂,这样又产生了和第一个方式同样的问题,
在线程中我们学过同步锁,我们可以简单的在:
public static synchronized ChocolateFactor getInstance(){ if (instance==null) { instance=new ChocolateFactor(); } return instance; }
但是我们知道加入synchronized 是非常耗内存的,所以这也不是我们的最终解决办法,然后我们再次看到另一个解决办法:
public class ChocolateFactor { private boolean isEmpty; private boolean isBoiled;// private static ChocolateFactor instance=null; private static ChocolateFactor instance=new ChocolateFactor(); private ChocolateFactor() { isEmpty=true; isBoiled=false; }// public static synchronized ChocolateFactor getInstance(){// if (instance==null) {// instance=new ChocolateFactor();// }// return instance;// } public static synchronized ChocolateFactor getInstance(){ return instance; } public void fill(){ if (isEmpty) { isEmpty=false; isBoiled=false; System.out.println("锅炉满了"); } } public void boil(){ if ((!isEmpty)&&(!isBoiled)) { isBoiled=true; System.out.println("锅炉已经沸腾了"); } } public void dump(){ if ((!isEmpty)&&(isBoiled)) { isEmpty=true; isBoiled=false; System.out.println("锅炉啥都没有咯"); } }}
可以看到和之前的代码不同的是我们直接在定义变量的时候就直接将工厂new出来,然后在getInstance中直接返回,这种方式成为懒汉式,但是问题又来了,假如我们不需要使用getInstance,然后在使用这个的类的时候我们就已经在变量中直接将工厂new了出来,所以这样也是浪费了内存,那么我们的最终解决办法是:
public static synchronized ChocolateFactor getInstance(){ if (instance==null) { synchronized (ChocolateFactor.class) { if (instance==null) { instance=new ChocolateFactor(); } } } return instance; }
使用双重判断加锁的方式,现在我们还是在多线程的状态,加入线程1还是在if (instance==null) { cpu不分配给它空间了,线程2开始工作,线程2一鼓作气将getInstance走完,现在线程1工作了,线程走到
if (instance==null) 方法instance已经不为null了,所以他将不会再次new 一个,所以这才是我们的终极方案。
—————————————————————————————————————————————
六、工厂模式
项目需求:生产披萨项目,生产披萨需要经过准备,烘烤,剪切,打包 4个步骤,根据客户输入的披萨名字生产对应的披萨。
首先用传统的方式去做:
public abstract class Pizza { protected String name; public abstract void prepare(); public void bake() { System.out.println(name+" baking;"); } public void cut() { System.out.println(name+" cutting;"); } public void box() { System.out.println(name+" boxing;"); } public void setname(String name) { this.name=name; }}
public class PepperPizza extends Pizza{ @Override public void prepare() { super.setname("PepperPizza"); System.out.println("PepperPizza"); }}
public class GreekPizza extends Pizza{ @Override public void prepare() { super.setname("GreekPizza"); System.out.println("GreekPizza"); }}
public class CheesePizza extends Pizza{ @Override public void prepare() { super.setname("CheesePizza"); System.out.println("CheesePizza"); }}
public class OrderPizza { OrderPizza(){ String type=null; Pizza pizza=null; do { type=getType(); if (type.equals("greek")) { pizza=new GreekPizza(); }else if (type.equals("cheese")) { pizza=new CheesePizza(); }else if (type.equals("pepper")) { pizza=new PepperPizza(); }else{ break; } pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); } while (true); } private String getType(){ String type=null; BufferedReader br=null; try { br=new BufferedReader(new InputStreamReader(System.in)); System.out.println("input pizza type:"); type= br.readLine(); } catch (IOException e) { e.printStackTrace(); return ""; } return type; }}
代码分析:首先我们考虑到生产披萨的4步骤是一样的,只有名字不一样,所以我们将名字方法抽象出来,友各个披萨类去实现,然后通过OrderPizza 类去模仿工厂生产的过程。
最后我们来测试一下:
public class test { public static void main(String[] args) { OrderPizza order1=new OrderPizza(); }}
然后现在又研究出了几个新的披萨品种,需要加入到系统中,那么如果继续在传统的方式中修改,我们只需要增加新品种披萨类,然后修改 OrderPizza 中的判断语句。
我们现在用简单工厂模式下试一下:
简单工厂类中各种披萨类还是一样,修改的是OrderPizza:
public class OrderPizza { OrderPizza(SimpleFactoryPizza factory){ String type=null; Pizza pizza=null; do { type=getType(); pizza=factory.createPizza(type); if (pizza!=null) { pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); }else{ break; } } while (true); } private String getType(){ String type=null; BufferedReader br=null; try { br=new BufferedReader(new InputStreamReader(System.in)); System.out.println("input pizza type:"); type= br.readLine(); } catch (IOException e) { e.printStackTrace(); return ""; } return type; }}
public class SimpleFactoryPizza { public Pizza createPizza(String type){ Pizza pizza=null; if (type.equals("greek")) { pizza=new GreekPizza(); }else if (type.equals("cheese")) { pizza=new CheesePizza(); }else if (type.equals("pepper")) { pizza=new PepperPizza(); }else if (type.equals("beef")) { pizza=new BeefPizza(); } return pizza; }}
虽然看似代码是一样的,但是我们已经将变化的代码抽取出来了,在OrderPizza中我们无需再次修改,此时我们已经将变化的和不变化的隔离开来了
然后现在我们需要在纽约,伦敦生产披萨,且各地生成的披萨都各有差异,那么我们现在使用工厂方法模式试试:
public class LDCheesePizza extends Pizza{ @Override public void prepare() { super.setname("LDCheesePizza"); System.out.println("LDCheesePizza"); }}
public class LDGreekPizza extends Pizza{ @Override public void prepare() { super.setname("LDGreekPizza"); System.out.println("LDGreekPizza"); }}
public class LDPepperPizza extends Pizza{ @Override public void prepare() { super.setname("LDPepperPizza"); System.out.println("LDPepperPizza"); }}
…纽约的也一样
我们再来看看OrderPizza
public abstract class OrderPizza { public abstract Pizza createPizza(String type); public OrderPizza(){ String type=null; Pizza pizza=null; do { type=getType(); pizza=createPizza(type); if (pizza!=null) { pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); }else{ break; } } while (true); } private String getType(){ String type=null; BufferedReader br=null; try { br=new BufferedReader(new InputStreamReader(System.in)); System.out.println("input pizza type:"); type= br.readLine(); } catch (IOException e) { e.printStackTrace(); return ""; } return type; }}
这里因为工厂是多样化的,所以这里我们将工厂抽象,然后看具体实现:
public class NewYorkOrderPizza extends OrderPizza { @Override public Pizza createPizza(String type) { Pizza pizza=null; if (type.equals("greek")) { pizza=new LDGreekPizza(); }else if (type.equals("cheese")) { pizza=new LDCheesePizza(); }else if (type.equals("pepper")) { pizza=new LDPepperPizza(); } return pizza; }}
public class LondonOrderPizza extends OrderPizza { @Override public Pizza createPizza(String type) { Pizza pizza=null; if (type.equals("greek")) { pizza=new NYGreekPizza(); }else if (type.equals("cheese")) { pizza=new NYCheesePizza(); }else if (type.equals("pepper")) { pizza=new NYPepperPizza(); } return pizza; }}
最后我们来测试一下
public class test { public static void main(String[] args) { OrderPizza order1=new NewYorkOrderPizza(); }}
最后我们再试试使用抽象工厂模式:
public interface FactoryPizza { Pizza createPizza(String type);}
单独将变化的工厂抽象
public class OrderPizza { OrderPizza(FactoryPizza factory){ String type=null; Pizza pizza=null; do { type=getType(); pizza=factory.createPizza(type); if (pizza!=null) { pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); }else{ break; } } while (true); } private String getType(){ String type=null; BufferedReader br=null; try { br=new BufferedReader(new InputStreamReader(System.in)); System.out.println("input pizza type:"); type= br.readLine(); } catch (IOException e) { e.printStackTrace(); return ""; } return type; }}
public class LondonFactoryPizza implements FactoryPizza{ @Override public Pizza createPizza(String type) { Pizza pizza=null; if (type.equals("greek")) { pizza=new LDGreekPizza(); }else if (type.equals("cheese")) { pizza=new LDCheesePizza(); }else if (type.equals("pepper")) { pizza=new LDPepperPizza(); } return pizza; }}
public class NewYorkOrderPizza implements FactoryPizza{ @Override public Pizza createPizza(String type) { Pizza pizza=null; if (type.equals("greek")) { pizza=new NYGreekPizza(); }else if (type.equals("cheese")) { pizza=new NYCheesePizza(); }else if (type.equals("pepper")) { pizza=new NYPepperPizza(); } return pizza; }}
效果和工厂方法模式是一样的,虽然这里可能看不出抽象工厂模式与工厂方法模式的差别,但是我们在大的项目就可以看出来了
—————————————————————————————————————————————
七、命令模式
项目需求:智能家居,用户使用手机遥控操作灯,录音机。
好的,还是先用传统的方式试试:
public class Light { String loc = ""; public Light(String loc) { this.loc = loc; } public void On() { System.out.println(loc + " On"); } public void Off() { System.out.println(loc + " Off"); }}
public class Stereo { static int volume = 0; public void On() { System.out.println("Stereo On"); } public void Off() { System.out.println("Stereo Off"); } public void SetCd() { System.out.println("Stereo SetCd"); } public void SetVol(int vol) { volume = vol; System.out.println("Stereo volume=" + volume); } public int GetVol() { return volume; } public void Start() { System.out.println("Stereo Start"); }}
public interface Control { public void onButton(int slot); public void offButton(int slot); public void undoButton();}
public class TraditionControl implements Control { Light light; Stereo stereo; public TraditionControl(Light light, Stereo stereo) { this.light = light; this.stereo = stereo; } @Override public void onButton(int slot) { // TODO Auto-generated method stub switch (slot) { case 0: light.On(); break; case 1: stereo.On(); break; case 2: int vol = stereo.GetVol(); if (vol < 11) { stereo.SetVol(++vol); } break; } } @Override public void offButton(int slot) { // TODO Auto-generated method stub switch (slot) { case 0: light.Off(); break; case 1: stereo.Off(); break; case 2: int vol = stereo.GetVol(); if (vol > 0) { stereo.SetVol(--vol); } break; } } @Override public void undoButton() { // TODO Auto-generated method stub }}
传统的设计的比较死,TraditionControl 通过 onButton 与offButton分别传入一个tag值去控制,这里使用了Control 接口,便于管理。
下面测试一下:
public class test { public static void main(String[] args) { Light light=new Light("bedroom"); Stereo stereo=new Stereo(); TraditionControl tc=new TraditionControl(light, stereo); tc.onButton(0); tc.offButton(0); tc.onButton(1); tc.offButton(1); tc.onButton(2); tc.offButton(2); tc.offButton(1); }}
现在假设录影机多了一个功能或者减少了一个功能,我们需要去修改 Stereo 类,与之也要修改TraditionControl,这里看到修改一个类另外一个类也需要修改,他们之间的耦合性太高了,所以下面使用命令模式:
public interface Command { public void execute(); public void undo();}
public class LightOffCommand implements Command { private Light light; public LightOffCommand(Light light) { this.light=light; } @Override public void execute() { // TODO Auto-generated method stub light.Off(); } @Override public void undo() { // TODO Auto-generated method stub light.On(); }}
public class LightOnCommand implements Command { private Light light; public LightOnCommand(Light light) { this.light=light; } @Override public void execute() { // TODO Auto-generated method stub light.On(); } @Override public void undo() { // TODO Auto-generated method stub light.Off(); }}
public class NoCommand implements Command { @Override public void execute() { // TODO Auto-generated method stub } @Override public void undo() { // TODO Auto-generated method stub }}
public class StereoAddVolCommand implements Command { private Stereo stereo; public StereoAddVolCommand(Stereo stereo) { this.stereo = stereo; } @Override public void execute() { // TODO Auto-generated method stub int vol = stereo.GetVol(); if (vol < 11) { stereo.SetVol(++vol); } } @Override public void undo() { // TODO Auto-generated method stub int vol = stereo.GetVol(); if (vol > 0) { stereo.SetVol(--vol); } }}
public class StereoSubVolCommand implements Command { private Stereo stereo; public StereoSubVolCommand(Stereo stereo) { this.stereo = stereo; } @Override public void execute() { // TODO Auto-generated method stub int vol = stereo.GetVol(); if (vol > 0) { stereo.SetVol(--vol); } } @Override public void undo() { // TODO Auto-generated method stub int vol = stereo.GetVol(); if (vol < 11) { stereo.SetVol(++vol); } }}
public class StereoOffCommand implements Command { private Stereo stereo; public StereoOffCommand(Stereo stereo) { this.stereo=stereo; } @Override public void execute() { // TODO Auto-generated method stub stereo.Off(); } @Override public void undo() { // TODO Auto-generated method stub stereo.On(); stereo.SetCd(); }}
public class StereoOnCommand implements Command { private Stereo stereo; public StereoOnCommand(Stereo stereo) { this.stereo=stereo; } @Override public void execute() { // TODO Auto-generated method stub stereo.On(); stereo.SetCd(); } @Override public void undo() { // TODO Auto-generated method stub stereo.Off(); }}
public class MarcoCommand implements Command { private Command[] commands; public MarcoCommand(Command[] commands) { this.commands = commands; } @Override public void execute() { // TODO Auto-generated method stub for (int i = 0, len = commands.length; i < len; i++) { commands[i].execute(); } } @Override public void undo() { // TODO Auto-generated method stub for (int i = commands.length - 1; i >= 0; i--) { commands[i].undo(); } }}
以上代码可以看出我们将每个命令单独写成一个类,如果需要增加新的命令也无需修改代码,只需类似上面去增加或者减少一个类
public class CommandModeControl implements Control{ private Command[] onCommands; private Command[] offCommands; private Stack<Command> stack=new Stack<Command>(); public CommandModeControl() { onCommands=new Command[5]; offCommands=new Command[5]; Command noCommand=new NoCommand(); for(int i=0,len=onCommands.length;i<len;i++) { onCommands[i]=noCommand; offCommands[i]=noCommand; } } public void setCommand(int slot,Command onCommand,Command offCommand) { onCommands[slot]=onCommand; offCommands[slot]=offCommand; } @Override public void onButton(int slot) { onCommands[slot].execute(); stack.push(onCommands[slot]); } @Override public void offButton(int slot) { offCommands[slot].execute(); stack.push(offCommands[slot]); } @Override public void undoButton() { // TODO Auto-generated method stub stack.pop().undo(); }}
CommandModeControl控制类还是继承自Control,通过其自身的setCommand方法将所需执行的命令添加到数组中,当然这里限定数组的大小是5,读者也可以写个1024之类的,假如需要的话。
下面看看测试的效果:
public class test { public static void main(String[] args) { Light bedroomLight=new Light("bedroom"); LightOnCommand lightOnCommand=new LightOnCommand(bedroomLight); LightOffCommand lightOffCommand=new LightOffCommand(bedroomLight); Stereo stereo=new Stereo(); StereoOnCommand stereoOnCommand=new StereoOnCommand(stereo); StereoOffCommand stereoOffCommand=new StereoOffCommand(stereo); StereoAddVolCommand addVolCommand=new StereoAddVolCommand(stereo); StereoSubVolCommand subVolCommand=new StereoSubVolCommand(stereo); Command[] commands1={lightOnCommand,stereoOnCommand,addVolCommand}; Command[] commands2={lightOffCommand,stereoOffCommand,subVolCommand}; MarcoCommand onMarcoCommand=new MarcoCommand(commands1); MarcoCommand offMarcoCommand=new MarcoCommand(commands2); CommandModeControl control=new CommandModeControl(); control.setCommand(0, lightOnCommand, lightOffCommand); control.onButton(0); control.offButton(0); control.setCommand(1, stereoOnCommand, stereoOffCommand); control.onButton(1); control.offButton(1); control.setCommand(2, addVolCommand, subVolCommand); control.onButton(2); control.onButton(2); control.onButton(2); control.offButton(2); control.undoButton(); control.setCommand(3, onMarcoCommand, offMarcoCommand); control.onButton(3); control.offButton(3); }}
通过使用命令模式可以看到类与类之间是完全没有耦合性的
—————————————————————————————————————————————
八、适配器模式
适配器通俗易懂相当于就是一个转换器,平时手机充电,假如这个头不能插入手机,我们只需要通过一个转换器,然后使用转换器上的插头即可。
现在有一个火鸡基地,他们想吃鸭子,他们就可以通过一个转换器:
public interface Turkey { public void gobble(); public void fly();}
public class WildTurkey implements Turkey { @Override public void gobble() { // TODO Auto-generated method stub System.out.println(" Go Go"); } @Override public void fly() { // TODO Auto-generated method stub System.out.println("I am flying a short distance"); }}
public interface Duck { public void quack(); public void fly();}
public class GreenHeadDuck implements Duck { @Override public void quack() { // TODO Auto-generated method stub System.out.println(" Ga Ga"); } @Override public void fly() { // TODO Auto-generated method stub System.out.println("I am flying a long distance"); }}
public class TurkeyAdapter implements Duck { private Turkey turkey; public TurkeyAdapter(Turkey turkey) { this.turkey=turkey; } @Override public void quack() { // TODO Auto-generated method stub turkey.gobble(); } @Override public void fly() { // TODO Auto-generated method stub for(int i=0;i<3;i++) { turkey.fly(); } }}
适配器通过实现鸭子类的方法,实现的是火鸡的方法,
下面我们来测试下:
public class test { public static void main(String[] args) { GreenHeadDuck greenHeadDuck=new GreenHeadDuck(); greenHeadDuck.quack(); greenHeadDuck.fly(); WildTurkey wildTurkey=new WildTurkey(); wildTurkey.gobble(); wildTurkey.fly(); TurkeyAdapter adapter=new TurkeyAdapter(wildTurkey); adapter.quack(); adapter.fly(); }}
这种适配器模式称为对象适配器,下面我们来介绍类适配器:
public class TurkeyAdapter extends WildTurkey implements Duck { @Override public void quack() { // TODO Auto-generated method stub super.gobble(); } @Override public void fly() { // TODO Auto-generated method stub super.fly(); super.fly(); super.fly(); }}
实现鸭子类并继承火鸡类,之前我们说过要少使用继承,所以我们这里还是推荐使用对象适配器。
来测试下:
public class test { public static void main(String[] args) { GreenHeadDuck greenHeadDuck=new GreenHeadDuck(); greenHeadDuck.quack(); greenHeadDuck.fly(); WildTurkey wildTurkey=new WildTurkey(); wildTurkey.gobble(); wildTurkey.fly(); TurkeyAdapter adapter=new TurkeyAdapter(); adapter.quack(); adapter.fly(); }}
可以看到效果是一摸一样,之前我们在java集合遍历使用的是枚举器,后来出现了迭代器,如果想用枚举器兼容迭代器,我们可以这样做:
public class EnumerationIterator implements Iterator<Object> { private Enumeration enumeration; public EnumerationIterator(Enumeration enumeration) { this.enumeration= enumeration; } @Override public boolean hasNext() { // TODO Auto-generated method stub return enumeration.hasMoreElements(); } @Override public Object next() { // TODO Auto-generated method stub return enumeration.nextElement(); } @Override public void remove() { // TODO Auto-generated method stub throw new UnsupportedOperationException(); }}
—————————————————————————————————————————————
九、外观模式
项目需求,家庭电影院,大概有一个这样的过程
直接用遥控器:统筹各设备开关
开爆米花机
放下屏幕
开投影仪
开音响
开DVD,选dvd
去拿爆米花
调暗灯光
播放
观影结束后,关闭各种设备
public class DVDPlayer { private static DVDPlayer instance = null; private DVDPlayer() { } public static DVDPlayer getInstance() { if (instance == null) { instance = new DVDPlayer(); } return instance; } public void on() { System.out.println("DVDPlayer On"); } public void off() { System.out.println("DVDPlayer Off"); } public void play() { System.out.println("DVDPlayer is playing"); } public void pause() { System.out.println("DVDPlayer pause"); } public void setdvd() { System.out.println("DVDPlayer is setting dvd"); }}
public class Popcorn { private static Popcorn instance = null; private Popcorn() { } public static Popcorn getInstance() { if (instance == null) { instance = new Popcorn(); } return instance; } public void on() { System.out.println("Popcorn On"); } public void off() { System.out.println("Popcorn Off"); } public void pop() { System.out.println("Popcorn is popping"); }}
public class Projector { private int size=5; private static Projector instance = null; private Projector() { } public static Projector getInstance() { if (instance == null) { instance = new Projector(); } return instance; } public void on() { System.out.println("Projector On"); } public void off() { System.out.println("Projector Off"); } public void focus() { System.out.println("Projector is focus"); } public void zoom(int size) { this.size=size; System.out.println("Projector zoom to"+size); }}
public class Screen { private static Screen instance = null; private Screen() { } public static Screen getInstance() { if (instance == null) { instance = new Screen(); } return instance; } public void up() { System.out.println("Screen up"); } public void down() { System.out.println("Screen down"); }}
public class Stereo { private static Stereo instance = null; private int volume = 5; private Stereo() { } public static Stereo getInstance() { if (instance == null) { instance = new Stereo(); } return instance; } public void on() { System.out.println("Stereo On"); } public void off() { System.out.println("Stereo Off"); } public void setVolume(int vol) { volume = vol; System.out.println("the volume of Stereo is set to " + volume); } public void addVolume() { if (volume < 11) { volume++; setVolume(volume); } } public void subVolume() { if (volume > 0) { volume--; setVolume(volume); } }}
public class TheaterLights { private static TheaterLights instance = null; private TheaterLights() { } public static TheaterLights getInstance() { if (instance == null) { instance = new TheaterLights(); } return instance; } public void on() { System.out.println("TheaterLights On"); } public void off() { System.out.println("TheaterLights Off"); } public void dim(int d) { System.out.println("TheaterLights dim to " + d + "%"); } public void bright() { dim(100); System.out.println("TheaterLights bright"); }}
以上就是将每个需要的功能封装成一个类具体实现,加入我们不使用外观模式,则需要当使用每个类的时候就要去调用相应的函数,我们看外观模式怎么实现:
public class HomeTheaterFacade { private TheaterLights mTheaterLights; private Popcorn mPopcorn; private Stereo mStereo; private Projector mProjector; private Screen mScreen; private DVDPlayer mDVDPlayer; public HomeTheaterFacade() { mTheaterLights = TheaterLights.getInstance(); mPopcorn = Popcorn.getInstance(); mStereo = Stereo.getInstance(); mProjector = Projector.getInstance(); mScreen = Screen.getInstance(); mDVDPlayer = DVDPlayer.getInstance(); } public void ready() { mPopcorn.on(); mPopcorn.pop(); mScreen.down(); mProjector.on(); mStereo.on(); mDVDPlayer.on(); mDVDPlayer.setdvd(); mTheaterLights.dim(10); } public void end() { mPopcorn.off(); mTheaterLights.bright(); mScreen.up(); mProjector.off(); mStereo.off(); mDVDPlayer.setdvd(); mDVDPlayer.off(); } public void play() { mDVDPlayer.play(); } public void pause() { mDVDPlayer.pause(); }}
从代码中可以看打我们是将一系列相同的操作封装在一个遥控器中,这样就不用每个功能去使用一个遥控器,大大简化了操作,外观模式遵循的是最少知识原则,所谓最少知识原则是将关联紧切的一些函数在一起使用,尽量减少对象之间的交互,只留几个“密友”。
测试一下:
public class test { public static void main(String[] args) { HomeTheaterFacade mHomeTheaterFacade=new HomeTheaterFacade(); mHomeTheaterFacade.ready(); mHomeTheaterFacade.play(); }}
—————————————————————————————————————————————
十、模板模式
冲咖啡和泡茶过程的描述:煮沸水,酝酿,倒入杯子,加入佐料,下面看代码:
public class Coffee { public void prepareRecipe(){ boilWater(); brewCoffee(); pourInCup(); addSugarMilk(); } private void addSugarMilk() { System.out.println("增加牛奶和糖"); } private void pourInCup() { System.out.println("倒入杯子"); } private void brewCoffee() { System.out.println("酝酿咖啡"); } private void boilWater() { System.out.println("煮沸水"); }}
public class Tea { public void prepareRecipe(){ boilWater(); brewCoffee(); pourInCup(); addLemon(); } private void addLemon() { System.out.println("增加柠檬"); } private void pourInCup() { System.out.println("倒入杯子"); } private void brewCoffee() { System.out.println("酝酿茶"); } private void boilWater() { System.out.println("煮沸水"); }}
其中泡茶和冲咖啡的煮沸水和倒入杯子步骤是相通的,然后最后在优化一下:
public class HotDrink { public void pourInCup() { System.out.println("倒入杯子"); } public void boilWater() { System.out.println("煮沸水"); }}
public class Coffee extends HotDrink{ public void prepareRecipe(){ super.boilWater(); brewCoffee(); super.pourInCup(); addSugarMilk(); } private void addSugarMilk() { System.out.println("增加牛奶和糖"); } private void brewCoffee() { System.out.println("酝酿咖啡"); }}
public class Tea extends HotDrink{ public void prepareRecipe(){ super.boilWater(); brewCoffee(); super.pourInCup(); addLemon(); } private void addLemon() { System.out.println("增加柠檬"); } private void brewCoffee() { System.out.println("酝酿茶"); }}
非常简单一个代码设计,测试一下:
public class test { public static void main(String[] args) { Coffee coffee=new Coffee(); coffee.prepareRecipe(); Tea tea=new Tea(); tea.prepareRecipe(); }}
我们在仔细对比一下会发现,冲咖啡和泡茶的4个步骤大致也是一致的,所以我们可以再次抽取:
public abstract class HotDrink { public final void prepareRecipe(){ boilWater(); brew(); pourInCup(); addCondiments(); } public final void boilWater() { System.out.println("煮沸水"); } public abstract void brew(); public final void pourInCup() { System.out.println("倒入杯子"); } public abstract void addCondiments();}
public class Coffee extends HotDrink{ @Override public void brew() { System.out.println("酝酿咖啡"); } @Override public void addCondiments() { System.out.println("增加糖和牛奶"); }}
public class Tea extends HotDrink{ @Override public void brew() { System.out.println("酝酿茶"); } @Override public void addCondiments() { System.out.println("增加柠檬"); }}
这就是使用了模板模式,将一系列算法的步骤或者流程封装在一个方法,将相同的步骤实现,不同的步骤抽象,更好的模板模式会提供一个钩子:
public abstract class HotDrink { public final void prepareRecipe(){ boilWater(); brew(); pourInCup(); if (isNeedCondiments()) { addCondiments(); }else{ System.out.println("not need condiments"); } } public final void boilWater() { System.out.println("煮沸水"); } public abstract void brew(); public final void pourInCup() { System.out.println("倒入杯子"); } public abstract void addCondiments(); public boolean isNeedCondiments(){ return true; }}
这里的isNeedCondiments()方法就是一个钩子,子类可以重写这个方法以判断是否需要:
public class Coffee extends HotDrink{ @Override public void brew() { System.out.println("酝酿咖啡"); } @Override public void addCondiments() { System.out.println("增加糖和牛奶"); } @Override public boolean isNeedCondiments() { String tagStr=null; boolean tag=true; System.out.println("请输入是否需要佐料:(y/n)"); BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); try { tagStr=br.readLine(); if (tagStr.equals("n")) { tag=false; } } catch (IOException e) { tag=false; e.printStackTrace(); } return tag; }}
这里咖啡重写钩子方法,通过用户输入y/n去判断是否需要佐料
在Java和android中其实很多地方用到了模板模式,将一个具体的算法步骤后者流程封装在一个方法体现出来,例如我们模拟燕子排序:
public class Duck implements Comparable<Duck>{ private int weight; private int height; public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } public Duck(int weight,int height) { this.weight=weight; this.height=height; } @Override public int compareTo(Duck o) { if (this.weight>o.weight) { return 1; }else{ return -1; } }}
public class test { public static void main(String[] args) { Duck[] ducks={new Duck(5, 6),new Duck(1, 2),new Duck(3, 4),new Duck(0, 0)}; bianli(ducks); Arrays.sort(ducks); System.out.println("排序后"); bianli(ducks); } private static void bianli(Duck[] ducks){ for (int i = 0; i < ducks.length; i++) { Duck duck=ducks[i]; System.out.println(duck.getWeight()); } }}
鸭子排序我们不需要排序的具体步骤,高层不需要知道底层的具体实现。
在Android中的BaseAdpater,通过getCount,getItem…等等一些列流程Andriod都将这个步骤怎么走的封装好了,再例如Activity的生命周期,Android也都将步骤都给封装好了。
—————————————————————————————————————————————
十一、迭代器模式
项目需求:蛋糕店和餐厅的菜单项目:
public class MenuItem { private String name,description; private boolean vegetable; private float price; public MenuItem(String name,String description,boolean vegetable,float price) { this.name=name; this.description=description; this.vegetable=vegetable; this.price=price; } public String getName() { return name; } public String getDescription() { return description; } public float getPrice() { return price; } public boolean isVegetable() { return vegetable; }}
public class CakeHouseMenu { private ArrayList<MenuItem> menuItems; public CakeHouseMenu() { menuItems = new ArrayList<MenuItem>(); addItem("KFC Cake Breakfast","boiled eggs&toast&cabbage",true,3.99f); addItem("MDL Cake Breakfast","fried eggs&toast",false,3.59f); addItem("Stawberry Cake","fresh stawberry",true,3.29f); addItem("Regular Cake Breakfast","toast&sausage",true,2.59f); } private void addItem(String name, String description, boolean vegetable, float price) { MenuItem menuItem = new MenuItem(name, description, vegetable, price); menuItems.add(menuItem); } public ArrayList<MenuItem> getMenuItems() { return menuItems; }}
public class DinerMenu { private final static int Max_Items=5; public int numberOfItems=0; private MenuItem[] menuItems; public DinerMenu() { menuItems=new MenuItem[Max_Items] ; addItem("vegetable Blt","bacon&lettuce&tomato&cabbage",true,3.58f); addItem("Blt","bacon&lettuce&tomato",false,3.00f); addItem("bean soup","bean&potato salad",true,3.28f); addItem("hotdog","onions&cheese&bread",false,3.05f); } private void addItem(String name, String description, boolean vegetable, float price) { MenuItem menuItem = new MenuItem(name, description, vegetable, price); if(numberOfItems>=Max_Items) { System.err.println("sorry,menu is full!can not add another item"); }else{ menuItems[numberOfItems]=menuItem; numberOfItems++; } } public MenuItem[] getMenuItems() { return menuItems; }}
分析代码:蛋糕店通过一个集合去增加菜单,提供公有的方法getMenuItems返回集合,而餐厅采用的是数组去增加菜单,并设置其最大的菜单数为5,提供公有的方法getMenuItems返回数组
我们再通过一个服务员去打印两个的菜单:
public class Waitress { private CakeHouseMenu mCakeHouseMenu; private DinerMenu mDinerMenu; private ArrayList<MenuItem> cakeitems; private MenuItem[] dineritems; public Waitress() { mCakeHouseMenu = new CakeHouseMenu(); cakeitems = mCakeHouseMenu.getMenuItems(); mDinerMenu = new DinerMenu(); dineritems = mDinerMenu.getMenuItems(); } public void printMenu() { MenuItem menuItem; for (int i = 0, len = cakeitems.size(); i < len; i++) { menuItem = cakeitems.get(i); System.out.println(menuItem.getName() + "***" +menuItem.getPrice()+"***"+ menuItem.getDescription()); } for (int i = 0, len = mDinerMenu.numberOfItems; i < len; i++) { menuItem = dineritems[i]; System.out.println(menuItem.getName() + "***" +menuItem.getPrice()+"***"+ menuItem.getDescription()); } }}
服务员通过他们的公共的方法获得餐厅和蛋糕店的菜单数组和集合,然后采用for循环遍历出来。
测试代码:
public class test { public static void main(String[] args) { Waitress mWaitress=new Waitress(); mWaitress.printMenu(); }}
现在我们需要将两个餐厅合并,但是我们可以看到一个是采用数组增加菜单,一个是采用集合的方式,所以现在引出了迭代器模式,提供一种方法顺序访问一个聚合对象中的各个对象
public interface Iterator { public boolean hasNext(); public Object next();}
public class CakeHouseMenu { private ArrayList<MenuItem> menuItems; public CakeHouseMenu() { menuItems = new ArrayList<MenuItem>(); addItem("KFC Cake Breakfast", "boiled eggs&toast&cabbage", true, 3.99f); addItem("MDL Cake Breakfast", "fried eggs&toast", false, 3.59f); addItem("Stawberry Cake", "fresh stawberry", true, 3.29f); addItem("Regular Cake Breakfast", "toast&sausage", true, 2.59f); } private void addItem(String name, String description, boolean vegetable, float price) { MenuItem menuItem = new MenuItem(name, description, vegetable, price); menuItems.add(menuItem); } public Iterator getIterator() { return new CakeHouseIterator(); } class CakeHouseIterator implements Iterator { private int position = 0; public CakeHouseIterator() { position = 0; } @Override public boolean hasNext() { // TODO Auto-generated method stub if (position < menuItems.size()) { return true; } return false; } @Override public Object next() { // TODO Auto-generated method stub MenuItem menuItem = menuItems.get(position); position++; return menuItem; } };}
public class DinerMenu { private final static int Max_Items = 5; private int numberOfItems = 0; private MenuItem[] menuItems; public DinerMenu() { menuItems = new MenuItem[Max_Items]; addItem("vegetable Blt", "bacon&lettuce&tomato&cabbage", true, 3.58f); addItem("Blt", "bacon&lettuce&tomato", false, 3.00f); addItem("bean soup", "bean&potato salad", true, 3.28f); addItem("hotdog", "onions&cheese&bread", false, 3.05f); } private void addItem(String name, String description, boolean vegetable, float price) { MenuItem menuItem = new MenuItem(name, description, vegetable, price); if (numberOfItems >= Max_Items) { System.err.println("sorry,menu is full!can not add another item"); } else { menuItems[numberOfItems] = menuItem; numberOfItems++; } } public Iterator getIterator() { return new DinerIterator(); } class DinerIterator implements Iterator { private int position; public DinerIterator() { position = 0; } @Override public boolean hasNext() { // TODO Auto-generated method stub if (position < numberOfItems) { return true; } return false; } @Override public Object next() { // TODO Auto-generated method stub MenuItem menuItem = menuItems[position]; position++; return menuItem; } };}
分析代码:之前我们是将各个店封装菜单的容器给提供出去,现在我们是将各种容器变成一个迭代接口,通过两个方法去遍历,所以现在外界无需知道我们的每个店具体是采用什么容器去封装菜单:
public class Waitress { private ArrayList<Iterator> iterators=new ArrayList<Iterator>(); public Waitress() { } public void addIterator(Iterator iterator) { iterators.add(iterator); } public void printMenu() { Iterator iterator; MenuItem menuItem; for (int i = 0, len = iterators.size(); i < len; i++) { iterator = iterators.get(i); while(iterator.hasNext()) { menuItem=(MenuItem) iterator.next(); System.out.println(menuItem.getName() + "***" +menuItem.getPrice()+"***"+ menuItem.getDescription()); } } }}
在蛋糕店和餐厅我们将他们的菜单容器都转化成一个迭代器,然后服务员类只需要得到他们各自转变后的迭代器,并使用迭代器提供的方法去遍历,无需知道具体的容器类型,也将餐厅,蛋糕店与服务员隔离,低耦合度,在之前我们是需要在服务员类中拿到两个店类,这样的话耦合度就高
现在来测试下:
public class MainTest { public static void main(String[] args) { Waitress mWaitress=new Waitress(); CakeHouseMenu mCakeHouseMenu = new CakeHouseMenu(); DinerMenu mDinerMenu = new DinerMenu(); mWaitress.addIterator(mCakeHouseMenu.getIterator()); mWaitress.addIterator(mDinerMenu.getIterator()); mWaitress.printMenu(); }}
最后我们在使用Java内置的迭代器试试,刚才我们自己定了一个接口Iterator,:
public class DinerMenu { private final static int Max_Items = 5; private int numberOfItems = 0; private MenuItem[] menuItems; public DinerMenu() { menuItems = new MenuItem[Max_Items]; addItem("vegetable Blt", "bacon&lettuce&tomato&cabbage", true, 3.58f); addItem("Blt", "bacon&lettuce&tomato", false, 3.00f); addItem("bean soup", "bean&potato salad", true, 3.28f); addItem("hotdog", "onions&cheese&bread", false, 3.05f); } private void addItem(String name, String description, boolean vegetable, float price) { MenuItem menuItem = new MenuItem(name, description, vegetable, price); if (numberOfItems >= Max_Items) { System.err.println("sorry,menu is full!can not add another item"); } else { menuItems[numberOfItems] = menuItem; numberOfItems++; } } public Iterator getIterator() { return new DinerIterator(); } class DinerIterator implements Iterator { private int position = 0; @Override public boolean hasNext() { // TODO Auto-generated method stub if (position < numberOfItems) { return true; } position = 0; return false; } @Override public Object next() { // TODO Auto-generated method stub MenuItem menuItem = menuItems[position]; position++; return menuItem; } @Override public void remove() { // TODO Auto-generated method stub } }}
public class CakeHouseMenu { private ArrayList<MenuItem> menuItems; private int position=0; public CakeHouseMenu() { menuItems = new ArrayList<MenuItem>(); addItem("KFC Cake Breakfast","boiled eggs&toast&cabbage",true,3.99f); addItem("MDL Cake Breakfast","fried eggs&toast",false,3.59f); addItem("Stawberry Cake","fresh stawberry",true,3.29f); addItem("Regular Cake Breakfast","toast&sausage",true,2.59f); } private void addItem(String name, String description, boolean vegetable, float price) { MenuItem menuItem = new MenuItem(name, description, vegetable, price); menuItems.add(menuItem); } public Iterator getIterator() { return menuItems.iterator(); }}
现在我们这是都是使用了Java内置的迭代器,因为ArrayList中本身有迭代器,所以代码中是直接返回。服务员类代码还是一样,这里就不继续贴出来了。
现在我们需要增加一个咖啡店的菜单:
public class CafeMenu { private Hashtable<String,MenuItem> menuItems=new Hashtable<String,MenuItem>(); public CafeMenu() { addItem("Moca Burger","moca&bruger&tomato",true,3.56f); addItem("Soup Latte","Latte&salad&soup",true,3.26f); addItem("Burrito","bacon&toamto&beans",false,3.96f); } private void addItem(String name, String description, boolean vegetable, float price) { MenuItem menuItem = new MenuItem(name, description, vegetable, price); menuItems.put(name, menuItem); } public Iterator getIterator() { return menuItems.values().iterator(); }}
该咖啡店采用的是Hashtable方式增加集合,但是通过迭代器模式我们不需要知道这个,可以很好的将新增的咖啡店菜单融合在一起。
—————————————————————————————————————————————
十二、组合模式
在上一节迭代模式中我们使用迭代器的方式将各个餐厅的菜单项结合在一起,现在我们继续在这个项目中增加需求,现在需要在菜单中添加一个子菜单,子菜单包括菜单项,首先下分析一下这个问题:
现在通过代码来看一下:
public abstract class MenuComponent { public String getName() { return ""; } public String getDescription() { return ""; } public float getPrice() { return 0; } public boolean isVegetable() { return false; } public abstract void print(); public Iterator getIterator() { return new NullIterator(); }}
public class MenuItem extends MenuComponent{ private String name,description; private boolean vegetable; private float price; public MenuItem(String name,String description,boolean vegetable,float price) { this.name=name; this.description=description; this.vegetable=vegetable; this.price=price; } @Override public String getName() { return name; } @Override public String getDescription() { return description; } @Override public float getPrice() { return price; } @Override public boolean isVegetable() { return vegetable; } @Override public void print() { // TODO Auto-generated method stub System.out.println(getName() + "***" + getPrice() + "***" + getDescription()); }}
public class NullIterator implements Iterator{ @Override public boolean hasNext() { // TODO Auto-generated method stub return false; } @Override public Object next() { // TODO Auto-generated method stub return null; } @Override public void remove() { // TODO Auto-generated method stub }}
分析代码:设定一个抽象的父类MenuComponent,让无论是菜单,还是菜单项MenuItem都去继承它,通过print去打印信息,getIterator去得到迭代器,如果遍历出来的MenuComponent是MenuItem则迭代器为NullIterator,和我们之前的命令模式中的NoCommand是一样的,表示没有迭代器,如果是菜单或者子菜单是会得到一个迭代器的,其他代码和迭代器模式中还是没有改变。
下面我们看看菜单和子菜单的代码:
public class CakeHouseMenu extends MenuComponent { private ArrayList<MenuComponent> menuItems; public CakeHouseMenu() { menuItems = new ArrayList<MenuComponent>(); addItem("KFC Cake Breakfast", "boiled eggs&toast&cabbage", true, 3.99f); addItem("MDL Cake Breakfast", "fried eggs&toast", false, 3.59f); addItem("Stawberry Cake", "fresh stawberry", true, 3.29f); addItem("Regular Cake Breakfast", "toast&sausage", true, 2.59f); } private void addItem(String name, String description, boolean vegetable, float price) { MenuItem menuItem = new MenuItem(name, description, vegetable, price); menuItems.add(menuItem); } public Iterator getIterator() { return new ComposeIterator(menuItems.iterator()); } @Override public void print() { // TODO Auto-generated method stub System.out.println("****This is CakeHouseMenu****"); };}
public class SubMenu extends MenuComponent { private ArrayList<MenuComponent> menuItems; public SubMenu() { menuItems = new ArrayList<MenuComponent>(); addItem("Apple Cookie", "Apple&candy&Cookie", true, 1.99f); addItem("Banana Cookie", "Banana&candy&Cookie", false, 1.59f); addItem("Orange Cookie", "Orange&Cookie", true, 1.29f); } private void addItem(String name, String description, boolean vegetable, float price) { MenuItem menuItem = new MenuItem(name, description, vegetable, price); menuItems.add(menuItem); } public Iterator getIterator() { return new ComposeIterator(menuItems.iterator()); } @Override public void print() { // TODO Auto-generated method stub System.out.println("****This is SubMenu****"); };}
public class DinerMenu extends MenuComponent { private final static int Max_Items = 5; private int numberOfItems = 0; private MenuComponent[] menuItems; public DinerMenu() { menuItems = new MenuComponent[Max_Items]; addItem("vegetable Blt", "bacon&lettuce&tomato&cabbage", true, 3.58f); addItem("Blt", "bacon&lettuce&tomato", false, 3.00f); addItem("bean soup", "bean&potato salad", true, 3.28f); addItem("hotdog", "onions&cheese&bread", false, 3.05f); addSubMenu(new SubMenu()); } private void addItem(String name, String description, boolean vegetable, float price) { MenuItem menuItem = new MenuItem(name, description, vegetable, price); if (numberOfItems >= Max_Items) { System.err.println("sorry,menu is full!can not add another item"); } else { menuItems[numberOfItems] = menuItem; numberOfItems++; } } private void addSubMenu(MenuComponent mMenuComponent) { if (numberOfItems >= Max_Items) { System.err.println("sorry,menu is full!can not add another item"); } else { menuItems[numberOfItems] = mMenuComponent; numberOfItems++; } } public Iterator getIterator() { return new ComposeIterator(new DinerIterator()); } class DinerIterator implements Iterator { private int position; public DinerIterator() { position = 0; } @Override public boolean hasNext() { // TODO Auto-generated method stub if (position < numberOfItems) { return true; } return false; } @Override public Object next() { // TODO Auto-generated method stub MenuComponent menuItem = menuItems[position]; position++; return menuItem; } @Override public void remove() { // TODO Auto-generated method stub } } @Override public void print() { // TODO Auto-generated method stub System.out.println("****This is DinerMenu****"); };}
代码分析:菜单,子菜单也继承自MenuComponent ,因为子菜单和菜单中包含MenuItem,所以它们都有各自的Iterator返回,Iterator的处理和之前的迭代器模式也是一样,只是我们现在通过了ComposeIterator类去包装它们的Iteraotr,而且转变成Iterator的各个菜单的菜单项容器装的也是MenuComponent ,这样的话,可以保持菜单中既可以装子菜单也可以装MenuItem,因为它们都是继承自父类MenuComponent ,这样做到了很好的延伸,,目前我们已经将迭代器封装好了,现在我们看看ComposeIterator的处理逻辑:
public class ComposeIterator implements Iterator { private Stack<Iterator> stack = new Stack<Iterator>(); public ComposeIterator(Iterator iterator) { stack.push(iterator); } @Override public boolean hasNext() { // TODO Auto-generated method stub if (stack.empty()) { return false; } Iterator iterator = stack.peek(); if (!iterator.hasNext()) { stack.pop(); return hasNext(); } else { return true; } } @Override public Object next() { // TODO Auto-generated method stub if (hasNext()) { Iterator iterator = stack.peek(); MenuComponent mMenuComponent = (MenuComponent) iterator.next(); stack.push(mMenuComponent.getIterator()); return mMenuComponent; } return null; } @Override public void remove() { // TODO Auto-generated method stub }}
ComposeIterator 继承自Iterator,在构造中传入菜单各自的Iterator,使用堆栈Stack push ,通过重写hasNext()去判断Stack 中的Iterator是否有元素,没有元素的话pop出去,然后在通过迭代hasNext()方法,继续判断,通过重写next()方法去返回Iterator中的每一个MenuComponent ,然后将每一个MenuComponent 的iterator 通过Stack push,假如MenuComponent 是SubMenu,则有Iterator,如果是MenuItem则我NullIterator,在hasNext()中pop掉。
下面我们看看服务员类怎么遍历:
public class Waitress { private ArrayList<MenuComponent> iterators = new ArrayList<MenuComponent>(); public Waitress() { } public void addComponent(MenuComponent mMenuComponent) { iterators.add(mMenuComponent); } public void printMenu() { Iterator iterator; MenuComponent menuItem; for (int i = 0, len = iterators.size(); i < len; i++) { iterators.get(i).print(); iterator = iterators.get(i).getIterator(); while (iterator.hasNext()) { menuItem = (MenuComponent) iterator.next(); menuItem.print(); } } } public void printBreakfastMenu() { } public void printLunchMenu() { } public void printVegetableMenu() { Iterator iterator; MenuComponent menuItem; for (int i = 0, len = iterators.size(); i < len; i++) { iterators.get(i).print(); iterator = iterators.get(i).getIterator(); while (iterator.hasNext()) { menuItem = (MenuComponent) iterator.next(); if (menuItem.isVegetable()) { menuItem.print(); } } } }}
在这次的Waitress 类我们添加的是MenuComponent ,假如MenuComponent 是MenuItem则,print会生效,在获取它的迭代器通过ComposeIterator 里面的判断逻辑hasNext()中会通过Stack pop掉,假如是SubMenu,则两者都会生效,这样就成功的将无论是子菜单还是菜单都可以打印出来。
下面来测试下:
public class MainTest { public static void main(String[] args) { Waitress mWaitress = new Waitress(); CakeHouseMenu mCakeHouseMenu = new CakeHouseMenu(); DinerMenu mDinerMenu = new DinerMenu(); mWaitress.addComponent(mCakeHouseMenu); mWaitress.addComponent(mDinerMenu); mWaitress.printMenu();; }}
当然Waitress 类还提供了printVegetableMenu方法,如果此MenuComponent 是蔬菜,则会打印。读者可以自己试试效果
最后总结下就是
组合模式:将对象聚合成树形结构来表现“整体/部分”的层次结构,
组合模式能让客户以一致 的形式处理对象或者对象组合
也就是我们可以忽略对象组合与个体对象的差别
—————————————————————————————————————————————
十三、状态模式
项目需求:糖果机项目,投硬币,退回硬币,转动转盘,糖果落下,假如糖果数量小于0了,还需要售罄状态。下面看代码:
public class CandyMachine { final static int SoldOutState = 0; final static int OnReadyState = 1; final static int HasCoin = 2; final static int SoldState = 3; private int state = SoldOutState; private int count = 0; public CandyMachine(int count) { this.count = count; if (count > 0) { state = OnReadyState; } } public void insertCoin() { switch (state) { case SoldOutState: System.out.println("you can't insert coin,the machine sold out!"); break; case OnReadyState: state = HasCoin; System.out .println("you have inserted a coin,next,please turn crank!"); break; case HasCoin: System.out.println("you can't insert another coin!"); break; case SoldState: System.out.println("please wait!we are giving you a candy!"); break; } } public void returnCoin() { switch (state) { case SoldOutState: System.out .println("you can't return,you haven't inserted a coin yet!"); break; case OnReadyState: System.out.println("you haven't inserted a coin yet!"); break; case HasCoin: System.out.println("coin return!"); state = OnReadyState; break; case SoldState: System.out.println("sorry,you already have turned the crank!"); break; } } public void turnCrank() { switch (state) { case SoldOutState: System.out.println("you turned,but there are no candies!"); break; case OnReadyState: System.out.println("you turned,but you haven't inserted a coin!"); break; case HasCoin: System.out.println("crank turn...!"); state = SoldState; dispense(); break; case SoldState: System.out .println("we are giving you a candy,turning another get nothing,!"); break; } } private void dispense() { count = count - 1; System.out.println("a candy rolling out!"); if (count > 0) { state = OnReadyState; } else { System.out.println("Oo,out of candies"); state = SoldOutState; } } public void printstate() { switch (state) { case SoldOutState: System.out.println("***SoldOutState***"); break; case OnReadyState: System.out.println("***OnReadyState***"); break; case HasCoin: System.out.println("***HasCoin***"); break; case SoldState: System.out.println("***SoldState***"); break; } }}
每个步骤封装成一个方法,每个步骤且都有4个状态的改变。并使用printstate打印状态。
下面来测试下:
public class MainTest { public static void main(String[] args) { CandyMachine mCandyMachine=new CandyMachine(2); mCandyMachine.printstate(); mCandyMachine.insertCoin(); mCandyMachine.printstate(); mCandyMachine.turnCrank(); mCandyMachine.printstate(); mCandyMachine.insertCoin(); mCandyMachine.printstate(); mCandyMachine.turnCrank(); mCandyMachine.printstate(); }}
现在加入游戏元素:在转动转盘过程中有%10的概率拿到2粒糖果
假如我们现在原始的基础上修改,需要在turnCrank()与dispense()步骤中加入赢家状态,并做相应的处理。
但是我们知道我们在设计代码的时候一定要遵守开闭原则,对于扩展是开放的,修改是关闭的。
我们现在来分析不变与变化的部分:变化的部分显然意见是状态,不变的是步骤,
每个状态都可能会促发这4个动作,所以我们得到下图:
将变化的状态封装成类,每个类促发这4个步骤,在糖果机类则定义状态,通过不同的状态去执行不同状态中的行为
状态模式:能根据内部状态的变化,改变对象的行为,看起来好像修改了类
下面看代码:
public interface State { public void insertCoin(); public void returnCoin(); public void turnCrank(); public void dispense(); public void printstate();}
public class OnReadyState implements State { private CandyMachine mCandyMachine; public OnReadyState(CandyMachine mCandyMachine) { this.mCandyMachine=mCandyMachine; } @Override public void insertCoin() { // TODO Auto-generated method stub System.out .println("you have inserted a coin,next,please turn crank!"); mCandyMachine.setState(mCandyMachine.mHasCoin); } @Override public void returnCoin() { // TODO Auto-generated method stub System.out.println("you haven't inserted a coin yet!"); } @Override public void turnCrank() { // TODO Auto-generated method stub System.out.println("you turned,but you haven't inserted a coin!"); } @Override public void dispense() { // TODO Auto-generated method stub } @Override public void printstate() { // TODO Auto-generated method stub System.out.println("***OnReadyState***"); }}
public class HasCoin implements State { private CandyMachine mCandyMachine; public HasCoin(CandyMachine mCandyMachine) { this.mCandyMachine = mCandyMachine; } @Override public void insertCoin() { // TODO Auto-generated method stub System.out.println("you can't insert another coin!"); } @Override public void returnCoin() { // TODO Auto-generated method stub System.out.println("coin return!"); mCandyMachine.setState(mCandyMachine.mOnReadyState); } @Override public void turnCrank() { // TODO Auto-generated method stub System.out.println("crank turn...!"); Random ranwinner=new Random(); int winner=ranwinner.nextInt(10); if(winner==0) { mCandyMachine.setState(mCandyMachine.mWinnerState); }else { mCandyMachine.setState(mCandyMachine.mSoldState); } } @Override public void dispense() { } @Override public void printstate() { // TODO Auto-generated method stub System.out.println("***HasCoin***"); }}
public class SoldState implements State { private CandyMachine mCandyMachine; public SoldState(CandyMachine mCandyMachine) { this.mCandyMachine=mCandyMachine; } @Override public void insertCoin() { // TODO Auto-generated method stub System.out.println("please wait!we are giving you a candy!"); } @Override public void returnCoin() { // TODO Auto-generated method stub System.out.println("you haven't inserted a coin yet!"); } @Override public void turnCrank() { // TODO Auto-generated method stub System.out .println("we are giving you a candy,turning another get nothing,!"); } @Override public void dispense() { // TODO Auto-generated method stub mCandyMachine.releaseCandy(); if (mCandyMachine.getCount() > 0) { mCandyMachine.setState(mCandyMachine.mOnReadyState); } else { System.out.println("Oo,out of candies"); mCandyMachine.setState(mCandyMachine.mSoldOutState); } } @Override public void printstate() { // TODO Auto-generated method stub System.out.println("***SoldState***"); }}
public class WinnerState implements State { private CandyMachine mCandyMachine; public WinnerState(CandyMachine mCandyMachine) { this.mCandyMachine = mCandyMachine; } @Override public void insertCoin() { // TODO Auto-generated method stub System.out.println("please wait!we are giving you a candy!"); } @Override public void returnCoin() { // TODO Auto-generated method stub System.out.println("you haven't inserted a coin yet!"); } @Override public void turnCrank() { // TODO Auto-generated method stub System.out .println("we are giving you a candy,turning another get nothing,!"); } @Override public void dispense() { // TODO Auto-generated method stub mCandyMachine.releaseCandy(); if (mCandyMachine.getCount() == 0) { mCandyMachine.setState(mCandyMachine.mSoldOutState); } else { System.out.println("you are a winner!you get another candy!"); mCandyMachine.releaseCandy(); if (mCandyMachine.getCount() > 0) { mCandyMachine.setState(mCandyMachine.mOnReadyState); } else { System.out.println("Oo,out of candies"); mCandyMachine.setState(mCandyMachine.mSoldOutState); } } } @Override public void printstate() { // TODO Auto-generated method stub System.out.println("***WinnerState***"); }}
public class SoldOutState implements State { private CandyMachine mCandyMachine; public SoldOutState(CandyMachine mCandyMachine) { this.mCandyMachine=mCandyMachine; } @Override public void insertCoin() { // TODO Auto-generated method stub System.out.println("you can't insert coin,the machine sold out!"); } @Override public void returnCoin() { // TODO Auto-generated method stub System.out .println("you can't return,you haven't inserted a coin yet!"); } @Override public void turnCrank() { // TODO Auto-generated method stub System.out.println("you turned,but there are no candies!"); } @Override public void dispense() { // TODO Auto-generated method stub } @Override public void printstate() { // TODO Auto-generated method stub System.out.println("***SoldOutState***"); }}
public class CandyMachine { State mSoldOutState; State mOnReadyState; State mHasCoin; State mSoldState; State mWinnerState; private State state; private int count = 0; public CandyMachine(int count) { this.count = count; mSoldOutState = new SoldOutState(this); mOnReadyState = new OnReadyState(this); mHasCoin = new HasCoin(this); mSoldState = new SoldState(this); mWinnerState = new WinnerState(this); if (count > 0) { state = mOnReadyState; } else { state = mSoldOutState; } } public void setState(State state) { this.state = state; } public void insertCoin() { state.insertCoin(); } public void returnCoin() { state.returnCoin(); } public void turnCrank() { state.turnCrank(); state.dispense(); } void releaseCandy() { // TODO Auto-generated method stub if (count > 0) { count = count - 1; System.out.println("a candy rolling out!"); } } public int getCount() { return count; } public void printstate() { state.printstate(); }}
代码分析:将每个状态单独抽取成类,加入新的状态只需要抽成类,实现对应的行为,糖果机的动作行为无需修改,这就做到了开闭原则。
下面来测试下:
public class MainTest { public static void main(String[] args) { CandyMachine mCandyMachine = new CandyMachine(6); mCandyMachine.printstate(); mCandyMachine.insertCoin(); mCandyMachine.printstate(); mCandyMachine.turnCrank(); mCandyMachine.printstate(); mCandyMachine.insertCoin(); mCandyMachine.printstate(); mCandyMachine.turnCrank(); mCandyMachine.printstate(); }}
测试代码和之前的并无太大差别当然如果运气好就可能有2粒糖果掉落的机会。
之前的策略模式中,鸭子项目中,我们将鸭子相同动作的不同行为抽取出来,我们这里也是将相同动作下的不同状态抽取出来,所以策略模式一般情况下可以作为状态模式的基础,但是在状态模式中,状态的改变会改变动作,而策略模式总的行为固定,动作不会改变了。在模板模式中,例如我们的泡茶,经过烧水,酝酿,倒入杯子,加入佐料,经过4个步骤,而我们的糖果机项目似乎也是经过几个步骤完成的,但是模板模式中的步骤是固定的,而状态模式中会随着状态改变去改变步骤。
—————————————————————————————————————————————
十四、代理模式
在上一个项目中老板要在各地都放置,所以想要监控各个糖果机,如果是在本地可以拿到糖果机对象:
public interface State{ public void insertCoin(); public void returnCoin(); public void turnCrank(); public void dispense(); public void printstate(); public String getstatename();}
public class SoldState implements State { private CandyMachine mCandyMachine; public SoldState(CandyMachine mCandyMachine) { this.mCandyMachine=mCandyMachine; } @Override public void insertCoin() { // TODO Auto-generated method stub System.out.println("please wait!we are giving you a candy!"); } @Override public void returnCoin() { // TODO Auto-generated method stub System.out.println("you haven't inserted a coin yet!"); } @Override public void turnCrank() { // TODO Auto-generated method stub System.out .println("we are giving you a candy,turning another get nothing,!"); } @Override public void dispense() { // TODO Auto-generated method stub mCandyMachine.releaseCandy(); if (mCandyMachine.getCount() > 0) { mCandyMachine.setState(mCandyMachine.mOnReadyState); } else { System.out.println("Oo,out of candies"); mCandyMachine.setState(mCandyMachine.mSoldOutState); } } @Override public void printstate() { // TODO Auto-generated method stub System.out.println("***SoldState***"); } @Override public String getstatename() { // TODO Auto-generated method stub return "SoldState"; }}
public class CandyMachine { State mSoldOutState; State mOnReadyState; State mHasCoin; State mSoldState; State mWinnerState; private String location=""; private State state; private int count = 0; public CandyMachine(String location,int count){ this.location=location; this.count = count; mSoldOutState = new SoldOutState(this); mOnReadyState = new OnReadyState(this); mHasCoin = new HasCoin(this); mSoldState = new SoldState(this); mWinnerState = new WinnerState(this); if (count > 0) { state = mOnReadyState; } else { state = mSoldOutState; } } public String getLocation() { return location; } public void setState(State state) { this.state = state; } public void insertCoin() { state.insertCoin(); } public void returnCoin() { state.returnCoin(); } public void turnCrank() { state.turnCrank(); state.dispense(); } void releaseCandy() { // TODO Auto-generated method stub if (count > 0) { count = count - 1; System.out.println("a candy rolling out!"); } } public int getCount() { return count; } public void printstate() { state.printstate(); } public State getstate() { return state; }}
之前的代码不变,在糖果机类中我们增加一个loc位置参数,并在State中增加一个获取状态的接口
public class Monitor { private ArrayList<CandyMachine> candyMachinelst; public Monitor() { candyMachinelst = new ArrayList<CandyMachine>(); } public void addMachine(CandyMachine mCandyMachine) { candyMachinelst.add(mCandyMachine); } public void report() { CandyMachine mCandyMachine; for (int i = 0, len = candyMachinelst.size(); i < len; i++) { mCandyMachine = candyMachinelst.get(i); System.out.println("Machine Loc:" + mCandyMachine.getLocation()); System.out.println("Machine Candy count:" + mCandyMachine.getCount()); System.out.println("Machine State:" + mCandyMachine.getstate().getstatename()); } }}
通过本地拿到糖果机对象遍历糖果机对象,report各个糖果机对象的信息。
测试代码:
Monitor mMonitor=new Monitor(); CandyMachine mCandyMachine = new CandyMachine("NY",6); mMonitor.addMachine(mCandyMachine); mCandyMachine = new CandyMachine("TK",4); mCandyMachine.insertCoin(); mMonitor.addMachine(mCandyMachine); mCandyMachine = new CandyMachine("Bj",14); mCandyMachine.insertCoin(); mCandyMachine.turnCrank(); mMonitor.addMachine(mCandyMachine); mMonitor.report();
这里我们是通过拿到各个地方的糖果机对象,才能得到各个糖果机对象的信息,但是一般我们是不可能拿到的,我们可能回想到用网络,通过网络实时传输数据到后台,通过后台去拿到糖果机对象的信息,但是我们这里要讲解的是代理模式,所以不考虑这种方法,
java中有一种rmi机制,能够让某个java虚拟机上的对象调用另一个Java虚拟机中的对象上的方法,通过远程代理:远程对象的本地代表,通过它可以让远程对象当本地对象来调用。
远程代理通过网络和真正的远程对象沟通信息。
代理模式:为一个对象提供一个替身,以控制对这个对象的访问
被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象
代理模式有很多变体,都是为了控制与管理对象访问
RMI远程方法调用是计算机之间通过网络实现对象调用的一种通讯机制。
使用这种机制,一台计算机上的对象可以调用另外 一台计算机上的对象来获取远
程数据。
在过去,TCP/IP通讯是远程通讯的主要手段,面向过程的开发。
而RPC使程序员更容易地调用远程程序,但在面对复杂的信息传讯时,RPC依然
未能很好的支持
RMI被设计成一种面向对象开发方式,允许程序员使用远程对象来实现通信
RMI的4个步骤:
制作远程接口:接口文件
远程接口的实现:Service文件
RMI服务端注册,开启服务
RMI代理端通过RMI查询到服务端,建立联系,通过接口调用远程方法
下面看一个RMI的例子:
public interface MyRemote extends Remote{ public String sayHello() throws RemoteException;}
@SuppressWarnings("serial")public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{ protected MyRemoteImpl() throws RemoteException { super(); // TODO Auto-generated constructor stub } @Override public String sayHello() throws RemoteException { // TODO Auto-generated method stub return "Hello World!"; } public static void main(String[] args) { try { MyRemote service=new MyRemoteImpl(); LocateRegistry.createRegistry(6600); Naming.rebind("rmi://127.0.0.1:6600/RemoteHello", service); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); System.out.println( e.toString()); } }}
public class MyRemoteClient { public static void main(String[] args) { new MyRemoteClient().go(); } public void go() { try { MyRemote service=(MyRemote)Naming.lookup("rmi://127.0.0.1:6600/RemoteHello"); String s=service.sayHello(); System.out.println(s); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } }}
代码分析:
首先定义出一个MyRemote 接口继承自Remote,声明出需要远程实现的方法,
然后在MyRemoteImpl 服务端类继承UnicastRemoteObject 并实现MyRemote 需要远程实现的方法,然后注册开启服务,
在MyRemoteClient 客户端寻找出对应的服务,调用远程需要实现的方法。
之前我们说我们拿不到糖果机对象,这个时候我们就可以使用RMI,通过建立一个远程代理对象实现远程监控,下面看代码:
public interface CandyMachineRemote extends Remote{ public String getLocation() throws RemoteException; public int getCount() throws RemoteException; public State getstate() throws RemoteException;}
public class CandyMachine extends UnicastRemoteObject implements CandyMachineRemote{ State mSoldOutState; State mOnReadyState; State mHasCoin; State mSoldState; State mWinnerState; private String location=""; private State state; private int count = 0; public CandyMachine(String location,int count) throws RemoteException{ this.location=location; this.count = count; mSoldOutState = new SoldOutState(this); mOnReadyState = new OnReadyState(this); mHasCoin = new HasCoin(this); mSoldState = new SoldState(this); mWinnerState = new WinnerState(this); if (count > 0) { state = mOnReadyState; } else { state = mSoldOutState; } } public String getLocation() { return location; } public void setState(State state) { this.state = state; } public void insertCoin() { state.insertCoin(); } public void returnCoin() { state.returnCoin(); } public void turnCrank() { state.turnCrank(); state.dispense(); } void releaseCandy() { // TODO Auto-generated method stub if (count > 0) { count = count - 1; System.out.println("a candy rolling out!"); } } public int getCount() { return count; } public void printstate() { state.printstate(); } public State getstate() { return state; }}
public interface State extends Serializable{ public void insertCoin(); public void returnCoin(); public void turnCrank(); public void dispense(); public void printstate(); public String getstatename();}
public class RemoteMainTest { public static void main(String[] args) { try { CandyMachine service = new CandyMachine("test1", 7); LocateRegistry.createRegistry(6602); Naming.rebind("rmi://127.0.0.1:6602/test1", service); service.insertCoin(); service = new CandyMachine("test2", 5); Naming.rebind("rmi://127.0.0.1:6602/test2", service); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); System.out.println(e.toString()); } }}
public class Monitor { private ArrayList<CandyMachineRemote> candyMachinelst; public Monitor() { candyMachinelst = new ArrayList<CandyMachineRemote>(); } public void addMachine(CandyMachineRemote mCandyMachine) { candyMachinelst.add(mCandyMachine); } public void report() { CandyMachineRemote mCandyMachine; for (int i = 0, len = candyMachinelst.size(); i < len; i++) { mCandyMachine = candyMachinelst.get(i); try { System.out .println("Machine Loc:" + mCandyMachine.getLocation()); System.out.println("Machine Candy count:" + mCandyMachine.getCount()); System.out.println("Machine State:" + mCandyMachine.getstate().getstatename()); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }}
public class MainTest { public static void main(String[] args) { Monitor mMonitor = new Monitor(); try { CandyMachineRemote mCandyMachine = (CandyMachineRemote) Naming .lookup("rmi://127.0.0.1:6602/test1"); mMonitor.addMachine(mCandyMachine); mCandyMachine = (CandyMachineRemote) Naming .lookup("rmi://127.0.0.1:6602/test2"); mMonitor.addMachine(mCandyMachine); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } mMonitor.report(); }}
代码分析:远程监控糖果机,
首先也是通过定义一个需要执行方法的接口CandyMachineRemote,
糖果机继承UnicastRemoteObject 并实现CandyMachineRemote,之前我们在本地监控器也已经也实现了,所以也这里无需添代码了。
RemoteMainTest 类作为服务类注册和开启远程代理,
MainTest 类作为客户端类寻找远程代理,
最后使用Monitor 类管理多个远程代理,也就是管理多个地点的糖果机对象。
最后看看效果吧:
动态代理:运行时动态的创建代理类,并将方法调用转发到指定类
看下代码:
public interface PersonBean { String getName(); String getGender(); String getInterests(); int getHotOrNotRating(); void setName(String name); void setGender(String gender); void setInterests(String interests); void setHotOrNotRating(int rating);}
public class PersonBeanImpl implements PersonBean{ String name; String gender; String interests; int rating; int ratingcount=0; @Override public String getName() { // TODO Auto-generated method stub return name; } @Override public String getGender() { // TODO Auto-generated method stub return gender; } @Override public String getInterests() { // TODO Auto-generated method stub return interests; } @Override public int getHotOrNotRating() { // TODO Auto-generated method stub if(ratingcount==0) return 0; return (rating/ratingcount); } @Override public void setName(String name) { // TODO Auto-generated method stub this.name=name; } @Override public void setGender(String gender) { // TODO Auto-generated method stub this.gender=gender; } @Override public void setInterests(String interests) { // TODO Auto-generated method stub this.interests=interests; } @Override public void setHotOrNotRating(int rating) { // TODO Auto-generated method stub this.rating=rating; ratingcount++; }}
public class NonOwnerInvocationHandler implements InvocationHandler{ PersonBean person; public NonOwnerInvocationHandler(PersonBean person) { this.person=person; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub if(method.getName().startsWith("get")) { return method.invoke(person,args); }else if(method.getName().equals("setHotOrNotRating")) { return method.invoke(person,args); }else if(method.getName().startsWith("set")) { return new IllegalAccessException(); } return null; }}
public class OwnerInvocationHandler implements InvocationHandler{ PersonBean person; public OwnerInvocationHandler(PersonBean person) { this.person=person; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // TODO Auto-generated method stub if(method.getName().startsWith("get")) { return method.invoke(person,args); }else if(method.getName().equals("setHotOrNotRating")) { return new IllegalAccessException(); }else if(method.getName().startsWith("set")) { return method.invoke(person,args); } return null; }}
public class MatchService { public MatchService() { PersonBean joe = getPersonInfo("joe", "male", "running"); PersonBean OwnerProxy = getOwnerProxy(joe); System.out.println("Name is " + OwnerProxy.getName()); System.out.println("Interests is " + OwnerProxy.getInterests()); OwnerProxy.setInterests("Bowling"); System.out.println("Interests are " + OwnerProxy.getInterests()); OwnerProxy.setHotOrNotRating(50); System.out.println("Rating is " + OwnerProxy.getHotOrNotRating()); OwnerProxy.setHotOrNotRating(40); System.out.println("Rating is " + OwnerProxy.getHotOrNotRating()); System.out.println("**************"); PersonBean nonOwnerProxy = getNonOwnerProxy(joe); System.out.println("Name is " + nonOwnerProxy.getName()); System.out.println("Interests are " + nonOwnerProxy.getInterests()); nonOwnerProxy.setInterests("haha"); System.out.println("Interests are " + nonOwnerProxy.getInterests()); nonOwnerProxy.setHotOrNotRating(60); System.out.println("Rating is " + nonOwnerProxy.getHotOrNotRating()); } PersonBean getPersonInfo(String name, String gender, String interests) { PersonBean person = new PersonBeanImpl(); person.setName(name); person.setGender(gender); person.setInterests(interests); return person; } PersonBean getOwnerProxy(PersonBean person) { return (PersonBean) Proxy.newProxyInstance(person.getClass() .getClassLoader(), person.getClass().getInterfaces(), new OwnerInvocationHandler(person)); } PersonBean getNonOwnerProxy(PersonBean person) { return (PersonBean) Proxy.newProxyInstance(person.getClass() .getClassLoader(), person.getClass().getInterfaces(), new NonOwnerInvocationHandler(person)); }}
public class MainTest { public static void main(String[] args) { MatchService mMatchService=new MatchService(); }}
—————————————————————————————————————————————
十五、复合模式
模式常一起使用,组合在一个设计解决方案中
复合模式在一个解决方案中结合两个或多个模式
能解决一般性或一系列的问题
某些模式结合使用,并不就是复合模式
MVC复合模式:
MVC:Model、View、Controller
Model:是程序主体,代表了业务数据和业
务逻辑
View:是与用户交互的界面,显示数据、接收输入,但不参与实际业务逻辑
Controller:接收用户输入,并解析反馈给Model
MVC里的模式:
Model与View和Controller是观察者
模式
View以组合模式管理控件
View与Controller是策略模式关系,
Controller提供策略
音乐播放器中的MVC:
View显示试图,用户点击View的试图,通过Controller中的Button事件反馈给Model,由Model去执行具体的逻辑和并更新数据
—————————————————————————————————————————————
十六、桥接模式
项目需求:遥控器项目,每个厂家的电视对应自己的遥控器。遥控器有打开,关闭,设置频道,设置音量。
使用传统的去设计:
public interface Control { public void On(); public void Off(); public void setChannel(int ch); public void setVolume(int vol);}
public class LGControl implements Control { @Override public void On() { // TODO Auto-generated method stub System.out.println("**Open LG TV**"); } @Override public void Off() { // TODO Auto-generated method stub System.out.println("**Off LG TV**"); } @Override public void setChannel(int ch) { // TODO Auto-generated method stub System.out.println("**The LG TV Channel is setted "+ch+"**"); } @Override public void setVolume(int vol) { // TODO Auto-generated method stub System.out.println("**The LG TV Volume is setted "+vol+"**"); }}
public class SonyControl implements Control { @Override public void On() { // TODO Auto-generated method stub System.out.println("*Open Sony TV*"); } @Override public void Off() { // TODO Auto-generated method stub System.out.println("*Off Sony TV*"); } @Override public void setChannel(int ch) { // TODO Auto-generated method stub System.out.println("*The Sony TV Channel is setted "+ch+"*"); } @Override public void setVolume(int vol) { // TODO Auto-generated method stub System.out.println("*The Sony TV Volume is setted "+vol+"*"); }}
public interface TvControl { public void Onoff(); public void nextChannel(); public void preChannel();}
public class LGTvControl extends LGControl implements TvControl{ private static int ch=0; private static boolean ison=false; public void Onoff() { if(ison) { ison=false; super.Off(); }else{ ison=true; super.On(); } } public void nextChannel() { ch++; super.setChannel(ch); } public void preChannel() { ch--; if(ch<0) { ch=200; } super.setChannel(ch); }}
代码分析:在底层使用Control 接口供各个厂商具体使用,在TvControl 中使用Control 接口方法供各个厂商实现具体行为。
测试代码:
public class MainTest { public static void main(String[] args) { LGTvControl mLGTvControl=new LGTvControl(); SonyTvControl mSonyTvControl=new SonyTvControl(); mLGTvControl.Onoff(); mLGTvControl.nextChannel(); mLGTvControl.nextChannel(); mLGTvControl.preChannel(); mLGTvControl.Onoff(); mSonyTvControl.Onoff(); mSonyTvControl.preChannel(); mSonyTvControl.preChannel(); mSonyTvControl.preChannel(); mSonyTvControl.Onoff(); }}
现在有了新需求:新的厂商需要在原先基础上增加新的供能,并且需要生产与该厂商对应的遥控器。
分析变化与不变:变化的是厂商需要增加新的功能,以及各个厂商对应各自的遥控器,各个遥控器的具体行为,不变的是各个遥控器具有的操作是相同的,厂商需要的功能中的操作是相同的,好的根据这个,我们来写代码:
public abstract class TvControlabs { Control mControl=null; public TvControlabs(Control mControl) { this.mControl=mControl; } public abstract void Onoff(); public abstract void nextChannel(); public abstract void preChannel();}
将厂商的操作抽象,以供新的厂商可以增加新的操作,并且通过传入Control 封装相应的操作,各个遥控器的操作是不变的,具体变得是操作的具体行为。
public class TvControl extends TvControlabs { private int ch=0; private boolean ison=false; public TvControl(Control mControl) { super(mControl); } @Override public void Onoff() { // TODO Auto-generated method stub if(ison) { ison=false; mControl.Off(); }else{ ison=true; mControl.On(); } } @Override public void nextChannel() { // TODO Auto-generated method stub ch++; mControl.setChannel(ch); } @Override public void preChannel() { // TODO Auto-generated method stub ch--; if(ch<0) { ch=200; } mControl.setChannel(ch); }}
TvControl 各个厂商只需要知道你传入的是什么遥控器来知道你是什么厂商
加入新的遥控器:
ublic class SharpControl implements Control { @Override public void On() { // TODO Auto-generated method stub System.out.println("***Open Sharp TV***"); } @Override public void Off() { // TODO Auto-generated method stub System.out.println("***Off Sharp TV***"); } @Override public void setChannel(int ch) { // TODO Auto-generated method stub System.out.println("***The Sharp TV Channel is setted "+ch+"***"); } @Override public void setVolume(int vol) { // TODO Auto-generated method stub System.out.println("***The Sharp TV Volume is setted "+vol+"***"); }}
遥控器的操作是相同的,变化的是具体的行为
加入新的厂商,并加入新功能:
public class newTvControl extends TvControlabs { private int ch=0; private boolean ison=false; private int prech=0; public newTvControl(Control mControl) { super(mControl); // TODO Auto-generated constructor stub } @Override public void Onoff() { // TODO Auto-generated method stub if(ison) { ison=false; mControl.Off(); }else{ ison=true; mControl.On(); } } @Override public void nextChannel() { // TODO Auto-generated method stub prech=ch; ch++; mControl.setChannel(ch); } @Override public void preChannel() { // TODO Auto-generated method stub prech=ch; ch--; if(ch<0) { ch=200; } mControl.setChannel(ch); } public void setChannel(int nch) { prech=ch; ch=nch; mControl.setChannel(ch); } public void Back() { mControl.setChannel(prech); }}
通过继承自抽象的 TvControlabs 加入新的功能。
桥接模式:将实现与抽象放在两个不同的类层次中,使两个层次可以独立改变
在我们这个项目中各个厂商TvControlabs 是抽象的,而各个遥控器Control 需要具体实现,这就是桥接模式
系统有多维角度分类时,而每一种分类又有可能变化,考虑使用桥接模式
桥接的目的是分离抽象与实现,使抽象和实现可以独立变化。
桥接模式与策略模式的差异:
桥接的目的是让底层实现和上层接口可以分别演化,从而提高移植性
策略的目的是将复杂的算法封装起来,从而便于替换不同的算法
桥接模式是往往是为了利用已有的方法或类
策略模式是为了扩展和修改,并提供动态配置
桥接模式强调接口对象仅提供基本操作
策略模式强调接口对象提供的是一种算法
—————————————————————————————————————————————
十七、生成器模式
生成器的英文是builder,也可以称为建造者
项目需求:度假计划生成项目
思考:一个完整的度假计划包括每天的度假情况,度假的日期,度假的天数,而其中每天的度假情况又可能包括度假住的酒店,度假的事件,度假的日期,度假的门票等,而在一个完整的度假计划中,度假的天数,日期,每天度假的情况又是变化的。
分析上面我们知道上面是一个对象包含另一个对象,另一个对象又包含另一个对象
上图是一个完整的度假计划对象包括几个度假天数对象
而上图中是由许多个不同的完整计划包括一个完整的度假计划,一个完整的度假计划对象包括几个度假天数对象
生成器模式:封装一个复杂对象构造过程,并允许按步骤构造。
上图就是一个标准的生成器执行过程,下面看代码:
public class VacationDay { private Date mDate; private String mHotels; private ArrayList<String> mTickets = null; private ArrayList<String> mEvents = null; public VacationDay(Date date) { mDate = date; mTickets = new ArrayList<String>(); mEvents = new ArrayList<String>(); } public void setDate(Date date) { mDate = date; } public void setHotel(String mHotels) { this.mHotels = mHotels; } public void addTicket(String ticket) { mTickets.add(ticket); } public void addEvent(String event) { mEvents.add(event); } public String showInfo() { StringBuilder stb = new StringBuilder(); stb.append("Date:" + mDate.toString() + "\n"); stb.append("Hotel:" + mHotels + "\n"); stb.append("Tickets:" + mTickets.toString() + "\n"); stb.append("Events" + mEvents.toString() + "\n"); return stb.toString(); }}
这是最基层的对象:每天的度假情况对象,包括这一天的度假日期,住的酒店,门票,发生的事件。
public class Vacation { private ArrayList<VacationDay> mVacationDayLst; private Date mStDate; private int mDays = 0; private VacationDay mVacationDay; public Vacation(String std) { mVacationDayLst = new ArrayList<VacationDay>(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); try { mStDate = sdf.parse(std); mVacationDay = new VacationDay(mStDate); mVacationDayLst.add(mVacationDay); mDays++; } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void addDay() { mVacationDay = new VacationDay(nextDate(mDays)); mVacationDayLst.add(mVacationDay); mDays++; } public void setHotel(String mHotels) { mVacationDay.setHotel(mHotels); } public void addTicket(String ticket) { mVacationDay.addTicket(ticket); } public void addEvent(String event) { mVacationDay.addEvent(event); } public void showInfo() { for (int i = 0, len = mVacationDayLst.size(); i < len; i++) { System.out.println("** " + (i + 1) + " day**"); System.out.println(mVacationDayLst.get(i).showInfo()); } } private Date nextDate(int n) { Calendar cal = Calendar.getInstance(); cal.setTime(mStDate); cal.add(Calendar.DATE, n); return cal.getTime(); }}
这是位于中间位置的对象:完整度假计划对象,包括度假天数,度假的日期,每天度假情况对象
public abstract class AbsBuilder { public Vacation mVacation; public AbsBuilder(String std) { mVacation = new Vacation(std); } public abstract void buildvacation(); public abstract void addHotel(String hotel); public abstract void addTicket(String ticket); public abstract void addEvent(String tvent); public Vacation getVacation() { return mVacation; }}
因为一个完整的度假计划,可能有3天的,4天的,所以将它抽象出来,暴露出一个完整的度假计划对象
public class Builder3d extends AbsBuilder { public Builder3d(String std) { super(std); // TODO Auto-generated constructor stub } @Override public void addHotel(String hotel) { // TODO Auto-generated method stub mVacation.setHotel(hotel); } @Override public void addTicket(String ticket) { // TODO Auto-generated method stub mVacation.addTicket(ticket); } @Override public void addEvent(String event) { // TODO Auto-generated method stub mVacation.addEvent(event); } @Override public void buildvacation() { // TODO Auto-generated method stub addTicket("Plane Ticket"); addEvent("Fly to Destination"); addEvent("Supper"); addEvent("Dancing"); addHotel("Four Seasons"); mVacation.addDay(); addTicket("Theme Park"); addEvent("Bus to Park"); addEvent("lunch"); addHotel("Four Seasons"); mVacation.addDay(); addTicket("Plane Ticket"); addEvent("City Tour"); addEvent("Fly to Home"); }}
public class Builder4d extends AbsBuilder { public Builder4d(String std) { super(std); // TODO Auto-generated constructor stub } @Override public void addHotel(String hotel) { // TODO Auto-generated method stub mVacation.setHotel(hotel); } @Override public void addTicket(String ticket) { // TODO Auto-generated method stub mVacation.addTicket(ticket); } @Override public void addEvent(String event) { // TODO Auto-generated method stub mVacation.addEvent(event); } @Override public void buildvacation() { // TODO Auto-generated method stub addTicket("Plane Ticket"); addEvent("Fly to Destination"); addEvent("Supper"); addHotel("Hilton"); mVacation.addDay(); addTicket("Zoo Ticket"); addEvent("Bus to Zoo"); addEvent("Feed animals"); addHotel("Hilton"); mVacation.addDay(); addTicket("Beach"); addEvent("Swimming"); addHotel("Home inn"); mVacation.addDay(); addTicket("Plane Ticket"); addEvent("Fly to Home"); }}
public class Director { private AbsBuilder builder; public Director(AbsBuilder builder) { this.builder=builder; } public void setBuilder(AbsBuilder builder) { this.builder=builder; } public void construct() { builder.buildvacation(); builder.getVacation().showInfo(); }}
Builder3d ,Builder4d 对象继承AbsBuilder,使用暴露的完整计划对象执行相应的操作,使用buildvacation()去封装某个完整的计划详情,再使用Director 类封装AbsBuilder ,通过construct()方法使一个完整的AbsBuilder 构建出来。
通过一层层的封装将一个复杂的对象过程简单化。
下面来测试下:
public class MainTest { public static void main(String[] args) { Director mDirector = new Director(new Builder4d("2017-12-29")); mDirector.construct(); mDirector.setBuilder(new Builder3d("2017-8-30")); mDirector.construct(); }
可以看到外面可以很便捷的使用。
在上面我们假设需要DIY,或者只有一个完整的度假计划,我们则可以省略抽象的生成器AbsBuilder 以及 指导者类Director 。看下面的代码:
public class BuilderSelf { public Vacation mVacation; public BuilderSelf(String std) { mVacation = new Vacation(std); // TODO Auto-generated constructor stub } public BuilderSelf addDay() { // TODO Auto-generated method stub mVacation.addDay(); return this; } public BuilderSelf addHotel(String hotel) { // TODO Auto-generated method stub mVacation.setHotel(hotel); return this; } public BuilderSelf addTicket(String ticket) { // TODO Auto-generated method stub mVacation.addTicket(ticket); return this; } public BuilderSelf addEvent(String event) { // TODO Auto-generated method stub mVacation.addEvent(event); return this; } public Vacation getVacation() { return mVacation; }}
测试代码:
public static void testself() { BuilderSelf builder = new BuilderSelf("2017-9-29"); builder.addTicket("Plane Ticket").addEvent("Fly to Destination") .addEvent("Supper").addHotel("Hilton"); builder.addDay().addTicket("Zoo Ticket").addEvent("Bus to Zoo") .addEvent("Feed animals").addHotel("Home Inn"); builder.addDay(); builder.addTicket("Beach"); builder.addEvent("Swimming"); builder.addHotel("Home inn"); builder.addDay().addTicket("Plane Ticket").addEvent("Fly to Home"); builder.getVacation().showInfo(); }
每次执行相应的返回都会返回对象本身,所以我们可以连续.的形式去调用。
由此我们联想到java中的StringBuilder也是通过生成器模式设计的。还有andorid的通知等等….
在我们之前学习过的工厂模式也是创建对象,
生成器模式:封装一个复杂对象构造过程,并允许按步骤构造。
优点:
将复杂对象的创建过程封装起来
允许对象通过几个步骤来创建,并且可以改变过程(工厂模式只有一个步骤)
只需指定具体生成器就能生成特定对象,隐藏类的内部结构
对象的实现可以被替换生成器模式和抽象工厂模式在功能上很相似,主要区别:
生成器一般用来创建大的复杂的对象
生成器模式强调的是一步步创建对象,可以改变步骤来生成不同的对象
一般来说生成器模式中对象不直接返回
—————————————————————————————————————————————
十八、责任链模式
项目需求:购买请求决策项目
购买请求决策项目介绍
决策因素:价格
决策级别:组长、部长、副总、总裁
我们建立一个Switch语句去判断价格,到底谁决策:
但是我们又由于某一天会减少个别决策级别,使用Switch需要修改里面的代码,便于扩展,所以有了下图:
通过价格首先将决策交给某个人,如果某个人不能决策又交给下个人,形成一个链式结构,每个Approver类中都设定一个Approver类,通过设定的Approver类而链接下一个Approver
通过ProcessRequest()方法去具体判断该Approver是否有执行权。
下图描述的是一个责任链的一个过程:
责任链模式:如果有多个对象都有机会处理请求,责任链可使请求的发送者和接收者解耦,请求沿着责任链传递,直到有一个对象处理了它为止。
责任链模式
优点:
将请求的发送者和接收者解耦,使多个对象都有机会处理这个请求
可以简化对象,因为它无须知道链的结构
可以动态地增加或删减处理请求的链结构
缺点:
请求从链的开头进行遍历,对性能有一定的损耗
并不保证请求一定被处理
好的,下面看代码:
public class PurchaseRequest { private int Type = 0; private int Number = 0; private float Price = 0; private int ID = 0; public PurchaseRequest(int Type, int Number, float Price) { this.Type = Type; this.Number = Number; this.Price = Price; } public int GetType() { return Type; } public float GetSum() { return Number * Price; } public int GetID() { return (int) (Math.random() * 1000); }}
public abstract class Approver { Approver successor; String Name; public Approver(String Name) { this.Name=Name; } public abstract void ProcessRequest( PurchaseRequest request); public void SetSuccessor(Approver successor) { // TODO Auto-generated method stub this.successor=successor; }}
public class GroupApprover extends Approver { public GroupApprover(String Name) { super(Name+" GroupLeader"); // TODO Auto-generated constructor stub } @Override public void ProcessRequest(PurchaseRequest request) { // TODO Auto-generated method stub if (request.GetSum() < 5000) { System.out.println("**This request " + request.GetID() + " will be handled by " + this.Name + " **"); } else { successor.ProcessRequest(request); } }}
public class DepartmentApprover extends Approver { public DepartmentApprover(String Name) { super(Name + " DepartmentLeader"); } @Override public void ProcessRequest(PurchaseRequest request) { // TODO Auto-generated method stub if ((5000 <= request.GetSum()) && (request.GetSum() < 10000)) { System.out.println("**This request " + request.GetID() + " will be handled by " + this.Name + " **"); } else { successor.ProcessRequest(request); } }}
public class PresidentApprover extends Approver { public PresidentApprover(String Name) { super(Name + " President"); } @Override public void ProcessRequest(PurchaseRequest request) { // TODO Auto-generated method stub if (50000 <= request.GetSum()) { System.out.println("**This request " + request.GetID() + " will be handled by " + this.Name + " **"); }else { successor.ProcessRequest(request); } }}
public class VicePresidentApprover extends Approver { public VicePresidentApprover(String Name) { super(Name + " Vice President"); } @Override public void ProcessRequest(PurchaseRequest request) { // TODO Auto-generated method stub if ((10000 <= request.GetSum()) && (request.GetSum() < 50000)) { System.out.println("**This request " + request.GetID() + " will be handled by " + this.Name + " **"); } else { successor.ProcessRequest(request); } }}
PurchaseRequest 类为决策请求,各个批准者通过继承抽象的Approver 实现ProcessRequest方法执行具体的判断,通过SetSuccessor可以让每个Approver 都链接上下一个Approver.
public class Client { public Client() { } public PurchaseRequest sendRequst(int Type, int Number, float Price) { return new PurchaseRequest(Type, Number, Price); }}
通过这个类模拟客户端发送决策请求,下面看下测试代码:
public class MainTest { public static void main(String[] args) { Client mClient=new Client(); Approver GroupLeader=new GroupApprover("Tom"); Approver DepartmentLeader=new DepartmentApprover("Jerry"); Approver VicePresident=new VicePresidentApprover("Kate"); Approver President=new PresidentApprover("Bush"); GroupLeader.SetSuccessor(DepartmentLeader); DepartmentLeader.SetSuccessor(VicePresident); VicePresident.SetSuccessor(President); President.SetSuccessor(GroupLeader); GroupLeader.ProcessRequest(mClient.sendRequst(1, 100, 40)); DepartmentLeader.ProcessRequest(mClient.sendRequst(2, 200, 40)); VicePresident.ProcessRequest(mClient.sendRequst(3, 300, 40)); President.ProcessRequest(mClient.sendRequst(4, 400, 140)); }}
在测试代码中我们首先需要将每个Approver准备链接的下一个Approver通过SetSuccessor链接好,这样才能实现链接的效果,在我们此测试代码中我们循环链接,所以就算无论我们怎么设置链接Approver,以及通过谁去ProcessRequest 得到的结果都是相同的。
学习过后大家可能会觉得责任链模式和状态模式没什么差别,责任链模式通过上面的价格不同而执行不同的链条,而状态模式通过状态不同执行不同的操作
责任链模式和状态模式主要区别:
责任链模式注重请求的传递
状态模式注重对象状态的转换责任链模式:如果有多个对象都有机会处理请求,责任链可使请求的发送者和接收者解耦,请求沿着责任链传递,直到有一个对象处理了它为止。
适用场合:
有多个对象可以处理一个请求
不明确接收者的情况
有序、无序链,线型、树形、环形链
—————————————————————————————————————————————
十九、蝇量模式
蝇量模式:蝇表示小,细粒的,量则是大量,大量的细粒的对象。
景观设计软件项目遇到的问题:
树:XY坐标,树的大小,外观,需要很多树
10000000棵树
我们先用最传统的设计一下:
public class Tree { private int xCoord, yCoord, age; public Tree(int xCoord, int yCoord, int age) { this.xCoord = xCoord; this.yCoord = yCoord; this.age = age; } public void display() { }}
创建一个树对象,display()显示树的信息,但由于10000000有点多,所以这里就不现实了
public class TreesTest { private int length = 10000000; private Tree[] treelst = new Tree[length]; public TreesTest() { for (int i = 0; i < length; i++) { treelst[i] = new Tree((int) (Math.random() * length), (int) (Math.random() * length), (int) (Math.random() * length) % 5); } } public void display() { for (int i = 0, len = treelst.length; i < len; i++) { treelst[i].display(); } }}
通过TreesTest 将10000000树对象构建出来,display遍历显示树的信息。
public class MainTest { public static void main(String[] args) { showMemInfo(); TreesTest mTreesTest; mTreesTest = new TreesTest(); showMemInfo(); mTreesTest.display(); showMemInfo(); } public static void showMemInfo() { // 最大内存: long max = Runtime.getRuntime().maxMemory(); // 分配内存: long total = Runtime.getRuntime().totalMemory(); // 已分配内存中的剩余空间 : long free = Runtime.getRuntime().freeMemory(); // 已占用的内存: long used = total - free; System.out.println("最大内存 = " + max); System.out.println("已分配内存 = " + total); System.out.println("已分配内存中的剩余空间 = " + free); System.out.println("已用内存 = " + used); System.out.println("时间 = " + System.currentTimeMillis()); System.out.println(""); }}
通过showMemInfo将程序运行所占用的内存打印出,在创建1千万树对象时之前之后分别打印,在display之后打印,下面看看结果:
可以看到在创建了1千万的树对象之后已用内存明显增多了许多,时间大概也用了4秒左右,而display内存和使用并无明显增加。
思考:这些树之间有啥关系?树与树都是树对象,只是他们需要显示的具体的坐标以及年龄有所差异也就是树的属性值,之前我们也可以看到display内存几乎无明显增长。
public class TreeFlyWeight { public TreeFlyWeight() { } public void display(int xCoord, int yCoord, int age) { // System.out.print("x"); }}
public class TreeManager { private int length = 10000000; int[] xArray = new int[length], yArray = new int[length], AgeArray = new int[length]; private TreeFlyWeight mTreeFlyWeight; public TreeManager() { mTreeFlyWeight = new TreeFlyWeight(); for (int i = 0; i < length; i++) { xArray[i] = (int) (Math.random() * length); yArray[i] = (int) (Math.random() * length); AgeArray[i] = (int) (Math.random() * length) % 5; } } public void displayTrees() { for (int i = 0; i < length; i++) { mTreeFlyWeight.display(xArray[i], yArray[i], AgeArray[i]); } }}
可以看到现在我们只创建了一个树对象,只是在display时1千万次。
然后我们现在看看内存:
public class MainTest { public static void main(String[] args) { showMemInfo(); TreeManager mTreeManager; mTreeManager = new TreeManager(); showMemInfo(); mTreeManager.displayTrees(); showMemInfo(); } public static void showMemInfo() { // 已分配内存中的剩余空间 : long free = Runtime.getRuntime().freeMemory(); // 分配内存: long total = Runtime.getRuntime().totalMemory(); // 最大内存: long max = Runtime.getRuntime().maxMemory(); // 已占用的内存: long used = total - free; System.out.println("最大内存 = " + max); System.out.println("已分配内存 = " + total); System.out.println("已分配内存中的剩余空间 = " + free); System.out.println("已用内存 = " + used); System.out.println("时间 = " + System.currentTimeMillis()); System.out.println(""); }}
和之前的图中内存明显要少了许多。
蝇量模式:通过共享的方式高效地支持大量细粒度的对象。
下面我们再来分析一张完整的蝇量模式图:
通过蝇量工厂中传入不同的key类型,得到不同的蝇量对象,蝇量对象以Operation显示蝇量的状态属性,通过ConcreteFlyweight去管理,下面我们在生产的1千万树中也需要既有树也有草对象,看代码:
public abstract class Plant { public Plant() { } public abstract void display(int xCoord, int yCoord, int age);}
public class Grass extends Plant { @Override public void display(int xCoord, int yCoord, int age) { // TODO Auto-generated method stub // System.out.print("Grass x"); }}
public class Tree extends Plant { @Override public void display(int xCoord, int yCoord, int age) { // TODO Auto-generated method stub // System.out.print("Tree x"); }}
通过抽象Plant 植物类,去构造草和树对象。
public class PlantFactory { private HashMap<Integer, Plant> plantMap = new HashMap<Integer, Plant>(); public PlantFactory() { } public Plant getPlant(int type) { if (!plantMap.containsKey(type)) { switch (type) { case 0: plantMap.put(0, new Tree()); break; case 1: plantMap.put(1, new Grass()); break; } } return plantMap.get(type); }}
通过PlantFactory 植物工厂传入key去获取相应的植物对象,这里传入0表示的树,1是草对象,而且通过HasHMap分别仅构造出一个植物对象
public class PlantManager { private int length = 10000000; private int[] xArray = new int[length], yArray = new int[length], AgeArray = new int[length], typeArray = new int[length]; private PlantFactory mPlantFactory; public PlantManager() { mPlantFactory=new PlantFactory(); for (int i = 0; i < length; i++) { xArray[i] = (int) (Math.random() * length); yArray[i] = (int) (Math.random() * length); AgeArray[i] = (int) (Math.random() * length) % 5; typeArray[i]= (int) (Math.random() * length) % 2; } } public void displayTrees() { for (int i = 0; i < length; i++) { mPlantFactory.getPlant(typeArray[i]).display(xArray[i], yArray[i], AgeArray[i]); } }}
和之前一样还是通过几个数组封装1千万植物的属性值,这里增加一个植物类型数组,当然其实也可以用二维数组,然后包含4个一维,但是发现二维用的内存更大,所以这里不考虑,然后还是和之前一样通过display遍历植物的属性值。
现在来测试下:
public class MainTest { public static void main(String[] args) { showMemInfo(); PlantManager mPlantManager; mPlantManager = new PlantManager(); showMemInfo(); mPlantManager.displayTrees(); showMemInfo(); } public static void showMemInfo() { // 已分配内存中的剩余空间 : long free = Runtime.getRuntime().freeMemory(); // 分配内存: long total = Runtime.getRuntime().totalMemory(); // 最大内存: long max = Runtime.getRuntime().maxMemory(); // 已占用的内存: long used = total - free; System.out.println("最大内存 = " + max); System.out.println("已分配内存 = " + total); System.out.println("已分配内存中的剩余空间 = " + free); System.out.println("已用内存 = " + used); System.out.println("时间 = " + System.currentTimeMillis()); System.out.println(""); }}
可以看到一个完整的蝇量模式可以容纳不同的对象并且显示,而且内存相比第一个传统的也要减少不少。
优点:
减少运行时的对象实例个数,节省创建开销和内存
将许多“虚拟”对象的状态集中管理
缺点:
系统设计更加复杂
需要专门维护对象的外部状态
适用场合:
需要大量细粒度对象
这些对象的外部状态不多,也就是我们例子中的属性。
按照内部状态分成几个组,每一个组都仅用一个蝇量对象代替
—————————————————————————————————————————————
二十、解释器模式
大数据统计项目遇到的问题:
按照计算模型对现有数据统计、分析、预测
一般的计算模型是一个或多个运算公式,通常是加减乘除四则运算
计算模型需要运行期编辑
设计方案要有高扩展性
思考怎么设计
首先计算模型里有两类符号:数据和计算符
我们计算模型的数据在数据库是固定的,是终结型,而计算模型中的计算符往往是根据我们人为的要求在运行期编辑,也就是对数据到底是加减乘除是变化的。
想到这里我们考虑到用逆波兰算法分析算式语法,到底逆波兰是怎么具体分析的,可以自行百度了解,使用逆波兰分析了算法格式之后,我们用我们定义好的解释器处理数据,解释器就是我们自行定义好的算法,例如这里加法,我们需要在解释器预先定义好。
解释器模式:定义一个语法, 定义一个解释器,该解释器处理该语法句子
将某些复杂问题,表达为某种语法规则,然后构建解释器来解释处理这类句子
通过客户端传入具体的语法,通过解释器具体下发处理。
上图便是我们例子中的项目解释器逻辑图。
通过逆波兰分析好语法格式后,通过Calculator 去让解释器去寻找对应的解释器,下面看具体代码:
public abstract class AbstractExpresstion { public abstract Float interpreter(HashMap<String, Float> var);}
通过HashMap键值对简单的模仿数据库
public class VarExpresstion extends AbstractExpresstion { private String key; public VarExpresstion(String _key) { this.key = _key; } @Override public Float interpreter(HashMap<String, Float> var) { // TODO Auto-generated method stub return var.get(this.key); }}
因为变量我们只需要通过Key得到它的数据。
public abstract class SymbolExpresstion extends AbstractExpresstion { protected AbstractExpresstion left; protected AbstractExpresstion right; public SymbolExpresstion(AbstractExpresstion _left, AbstractExpresstion _right) { this.left = _left; this.right = _right; }}
而计算符我们并不知道左边的解和右边的解释各是什么。
public class AddExpresstion extends SymbolExpresstion { public AddExpresstion(AbstractExpresstion _left, AbstractExpresstion _right) { super(_left, _right); // TODO Auto-generated constructor stub } @Override public Float interpreter(HashMap<String, Float> var) { // TODO Auto-generated method stub return super.left.interpreter(var) + super.right.interpreter(var); }}
public class DivExpresstion extends SymbolExpresstion { public DivExpresstion(AbstractExpresstion _left, AbstractExpresstion _right) { super(_left, _right); // TODO Auto-generated constructor stub } @Override public Float interpreter(HashMap<String, Float> var) { // TODO Auto-generated method stub return super.left.interpreter(var) / super.right.interpreter(var); }}
public class MultiExpresstion extends SymbolExpresstion { public MultiExpresstion(AbstractExpresstion _left, AbstractExpresstion _right) { super(_left, _right); // TODO Auto-generated constructor stub } @Override public Float interpreter(HashMap<String, Float> var) { // TODO Auto-generated method stub return super.left.interpreter(var) * super.right.interpreter(var); }}
public class SubExpresstion extends SymbolExpresstion { public SubExpresstion(AbstractExpresstion _left, AbstractExpresstion _right) { super(_left, _right); // TODO Auto-generated constructor stub } @Override public Float interpreter(HashMap<String, Float> var) { // TODO Auto-generated method stub return super.left.interpreter(var) - super.right.interpreter(var); }}
在加减乘除解释器中通过重写interpreter()方法去计算,具体怎是通过构造函数传入的解释器具体再执行interpreter()方法,假如是var解释器,则直接得到变量,假如是其他的乘等,则需要再次进入乘的interpreter()方法,可以看出这是一个迭代的方式计算。
下面看看逆波兰分析算法:
public class RPN { private ArrayList<String> expression = new ArrayList<String>();// 存储中序表达式 private ArrayList<String> right = new ArrayList<String>();// 存储右序表达式 private AbstractExpresstion result;// 结果 // 依据输入信息创建对象,将数值与操作符放入ArrayList中 public RPN(String input) { StringTokenizer st = new StringTokenizer(input, "+-*/()", true); while (st.hasMoreElements()) { expression.add(st.nextToken()); } } // 将中序表达式转换为右序表达式 private void toRight() { Stacks aStack = new Stacks(); String operator; int position = 0; while (true) { if (Calculate.isOperator((String) expression.get(position))) { if (aStack.top == -1 || ((String) expression.get(position)).equals("(")) { aStack.push(expression.get(position)); } else { if (((String) expression.get(position)).equals(")")) { if (!((String) aStack.top()).equals("(")) { operator = (String) aStack.pop(); right.add(operator); } } else { if (Calculate.priority((String) expression .get(position)) <= Calculate .priority((String) aStack.top()) && aStack.top != -1) { operator = (String) aStack.pop(); if (!operator.equals("(")) right.add(operator); } aStack.push(expression.get(position)); } } } else right.add(expression.get(position)); position++; if (position >= expression.size()) break; } while (aStack.top != -1) { operator = (String) aStack.pop(); right.add(operator); } } // 对右序表达式进行求值 public void getResult(HashMap<String, Float> var) { this.toRight(); Stack<AbstractExpresstion> stack = new Stack<AbstractExpresstion>(); AbstractExpresstion op1, op2; String is = null; Iterator it = right.iterator(); while (it.hasNext()) { is = (String) it.next(); if (Calculate.isOperator(is)) { op2 = stack.pop(); op1 = stack.pop(); stack.push(Calculate.twoResult(is, op1, op2)); } else stack.push(new VarExpresstion(is)); } result = stack.pop(); it = expression.iterator(); while (it.hasNext()) { System.out.print((String) it.next()); } System.out.println("=" + result.interpreter(var)); } public static class Calculate { // 判断是否为操作符号 public static boolean isOperator(String operator) { if (operator.equals("+") || operator.equals("-") || operator.equals("*") || operator.equals("/") || operator.equals("(") || operator.equals(")")) return true; else return false; } // 设置操作符号的优先级别 public static int priority(String operator) { if (operator.equals("+") || operator.equals("-") || operator.equals("(")) return 1; else if (operator.equals("*") || operator.equals("/")) return 2; else return 0; } // 做2值之间的计算 public static AbstractExpresstion twoResult(String op, AbstractExpresstion a, AbstractExpresstion b) { try { AbstractExpresstion result = null; if (op.equals("+")) result = new AddExpresstion(a, b); else if (op.equals("-")) result = new SubExpresstion(a, b); else if (op.equals("*")) result = new MultiExpresstion(a, b); else if (op.equals("/")) result = new DivExpresstion(a, b); else ; return result; } catch (NumberFormatException e) { System.out.println("input has something wrong!"); return null; } } } // 栈类 public class Stacks { private LinkedList list = new LinkedList(); int top = -1; public void push(Object value) { top++; list.addFirst(value); } public Object pop() { Object temp = list.getFirst(); top--; list.removeFirst(); return temp; } public Object top() { return list.getFirst(); } }}
首先在构造通过expression将分隔开来的计算模型存起来,toRight则是将右序表达式痛殴right集合存起来,getResult将结果计算出来,Calculate类则是做具体的分析,通过twoResult()去拿到具体的解释器,Stacks 类则是起到了承接的作用,将逆波兰分析的语法push,在需要的时候pop出来。
下面我们看看具体的客户端:
public class Calculator { public Calculator() { float[][] dataSource = new float[3][6]; System.out.println("data source:"); for (int i = 0; i < 3; i++) { for (int j = 0; j < 6; j++) { dataSource[i][j] = (float) (Math.random() * 100); System.out.print(dataSource[i][j] + ","); } System.out.println(";"); } try { System.out.println("Input a expression:"); BufferedReader is = new BufferedReader(new InputStreamReader( System.in)); for (;;) { String input = new String(); input = is.readLine().trim(); if (input.equals("q")) break; else { RPN boya = new RPN(input); HashMap<String, Float> var; for (int i = 0; i < 3; i++) { var = new HashMap<String, Float>(); var.put("a", dataSource[i][0]); var.put("b", dataSource[i][1]); var.put("c", dataSource[i][2]); var.put("d", dataSource[i][3]); var.put("e", dataSource[i][4]); var.put("f", dataSource[i][5]); boya.getResult(var); } } System.out .println("Input another expression or input 'q' to quit:"); } is.close(); } catch (IOException e) { System.out.println("Wrong input!!!"); } }}
客户端则是简单的创建除了3组,每组6个数组的HashMap集合,通过控制台输入的计算模型,传入给逆波兰分析算法,在通过最后的getResult传给解释器处理处理。
public class MainTest { public static void main(String[] args) { new Calculator(); }}
测试代码只需要将客户端Calculator类new出来通过在控制台输入计算模型则会拿到相应的统计结果:
解释器模式:定义一个语法, 定义一个解释器,该解释器处理该语法句子
优点:
容易修改,修改语法规则只要修改相应非终结符即可
扩展方便,扩展语法,只要增加非终结符类即可
缺点:
对于复杂语法的表示会产生复杂的类层次结构,不便管理和维护
解释器采用递归方式,效率会受影响注意事项:
尽量不要在重要的模块中使用解释器模式
解释器模式在实际的系统开发中使用的非常少
可以考虑一下Expression4J、MESP、Jep等开源的解析工具包适用场合: 当你有一个简单语法,而且效率不是问题的时候 一些数据分析工具、报表设计工具、科学计算工具等
—————————————————————————————————————————————
二十一、中介者模式
智慧房屋公司的产品:
闹钟、咖啡机、电视机、窗帘等,当主人需要起床时闹钟提醒主人,主人出去后,闹钟唤醒其他电器关掉,在一定时间闹钟唤醒咖啡机煮咖啡,打开电视,过几分钟又唤醒关电视等等。
首先我们想到这么以类图:
每个产品之间串联唤醒,新的需求,加入一个新的产品需要修改每个产品的唤醒结构。
思考如何设计:
各对象有几种状态改变
相互作用如何
上图通过建立一个中介者,各产品发送状态给中介者,中介者再使需要唤醒的产品改变状态。这样在需要新增产品时也就无需修改每个产品,只需要在中介者中修改相应的状态。
中介者模式:用一个中介对象来封装一系列的对象交互。
中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互
上图是一个完整的中介者模式类图,对于图来说它是相对一个较大的架构设计的,它首先将中介者抽象出来了Mediator,ConcreteMediator继承自它,而在本例子中当然我们需要一个中介者,为了以后更好的扩展我也可以像图例所示,定义出Mediator抽象类。然后图中定义了抽线的Colleague类,在Colleague中放入Mediator,然后具体的Colleague类继承自Colleague,通过传入的Mediator去处理事件。
下面看看具体的代码:
public interface Mediator { public abstract void Register(String colleagueName, Colleague colleague); public abstract void GetMessage(int stateChange, String colleagueName);}
public class ConcreteMediator implements Mediator { private HashMap<String, Colleague> colleagueMap; private HashMap<String, String> interMap; public ConcreteMediator() { colleagueMap = new HashMap<String, Colleague>(); interMap = new HashMap<String, String>(); } @Override public void Register(String colleagueName, Colleague colleague) { // TODO Auto-generated method stub colleagueMap.put(colleagueName, colleague); // TODO Auto-generated method stub if (colleague instanceof Alarm) { interMap.put("Alarm", colleagueName); } else if (colleague instanceof CoffeeMachine) { interMap.put("CoffeeMachine", colleagueName); } else if (colleague instanceof TV) { interMap.put("TV", colleagueName); } else if (colleague instanceof Curtains) { interMap.put("Curtains", colleagueName); } } @Override public void GetMessage(int stateChange, String colleagueName) { // TODO Auto-generated method stub if (colleagueMap.get(colleagueName) instanceof Alarm) { if (stateChange == 0) { ((CoffeeMachine) (colleagueMap.get(interMap .get("CoffeeMachine")))).StartCoffee(); ((TV) (colleagueMap.get(interMap.get("TV")))).StartTv(); } else if (stateChange == 1) { ((TV) (colleagueMap.get(interMap.get("TV")))).StopTv(); } } else if (colleagueMap.get(colleagueName) instanceof CoffeeMachine) { ((Curtains) (colleagueMap.get(interMap.get("Curtains")))) .UpCurtains(); } else if (colleagueMap.get(colleagueName) instanceof TV) { } else if (colleagueMap.get(colleagueName) instanceof Curtains) { } }}
Mediator 定义了两个方法,一个注册Colleague,另一个得到Colleague发送过来的状态消息,
实际的Mediator类ConcreteMediator 类通过colleagueMap将注册的Colleague,通过它提供的colleagueName为key的形式保存起来,在内部判断传入的Colleague是哪个实例,然后以自己定义的key通过interMap保存起来,以便到时候需要某个Colleague时可以通过interMap的key取出来。
而通过GetMessage处理各Colleague发送过来的状态消息,首先通过colleagueMap根据传入的colleagueName取出对应的实例,然后再根据传入的stateChange值去做相应的唤醒,通过interMap的Key可以拿到我们需要的Colleague,这就是这里interMap的作用。
然后我们看看Colleague:
public abstract class Colleague { private Mediator mediator; public String name; public Colleague(Mediator mediator, String name) { this.mediator = mediator; this.name = name; } public Mediator GetMediator() { return this.mediator; } public abstract void SendMessage(int stateChange);}
public class Alarm extends Colleague { public Alarm(Mediator mediator, String name) { super(mediator, name); // TODO Auto-generated constructor stub mediator.Register(name, this); } public void SendAlarm(int stateChange) { SendMessage(stateChange); } @Override public void SendMessage(int stateChange) { // TODO Auto-generated method stub this.GetMediator().GetMessage(stateChange, this.name); }}
public class CoffeeMachine extends Colleague { public CoffeeMachine(Mediator mediator, String name) { super(mediator, name); // TODO Auto-generated constructor stub mediator.Register(name, this); } @Override public void SendMessage(int stateChange) { // TODO Auto-generated method stub this.GetMediator().GetMessage(stateChange, this.name); } public void StartCoffee() { System.out.println("It's time to startcoffee!"); } public void FinishCoffee() { System.out.println("After 5 minutes!"); System.out.println("Coffee is ok!"); SendMessage(0); }}
public class Curtains extends Colleague { public Curtains(Mediator mediator, String name) { super(mediator, name); // TODO Auto-generated constructor stub mediator.Register(name, this); } @Override public void SendMessage(int stateChange) { // TODO Auto-generated method stub this.GetMediator().GetMessage(stateChange, this.name); } public void UpCurtains() { System.out.println("I am holding Up Curtains!"); }}
public class TV extends Colleague { public TV(Mediator mediator, String name) { super(mediator, name); // TODO Auto-generated constructor stub mediator.Register(name, this); } @Override public void SendMessage(int stateChange) { // TODO Auto-generated method stub this.GetMediator().GetMessage(stateChange, this.name); } public void StartTv() { // TODO Auto-generated method stub System.out.println("It's time to StartTv!"); } public void StopTv() { // TODO Auto-generated method stub System.out.println("StopTv!"); }}
首先在各个具体的Colleague类的构造中需要通过mediator.Register(name, this),将Colleague注册并在mediator中保存,通过SendMessage去改变各自的状态,然后在在需要功能的Colleague中定义了各自的函数。
现在我们来测试下:
public class MainTest { public static void main(String[] args) { Mediator mediator = new ConcreteMediator(); Alarm mAlarm = new Alarm(mediator, "mAlarm"); CoffeeMachine mCoffeeMachine = new CoffeeMachine(mediator, "mCoffeeMachine"); Curtains mCurtains = new Curtains(mediator, "mCurtains"); TV mTV = new TV(mediator, "mTV"); mAlarm.SendAlarm(0); mCoffeeMachine.FinishCoffee(); mAlarm.SendAlarm(1); }}
首先闹钟发送一个0状态的消息给Mediator,Mediator接收到首先判断这个实例是闹钟,然后根据闹钟的状态,执行相应的唤醒,开始煮咖啡和打开电视,然后咖啡机又调用自己的FinishCoffee()方法结束煮咖啡,并发送了一个0状态的给Mediator,Mediator根据传入的实例发现时咖啡机实例,然后根据状态去将窗帘拉下,最后闹钟在发送1的状态给Mediator,Mediator将电视机关闭。
中介者模式 优点:
通过将对象彼此解耦,可以增加对象的复用性
通过将控制逻辑集中,可以简化系统维护
可以让对象之间所传递的消息变得简单而且大幅减少
提高系统的灵活性,使得系统易于扩展和维护
缺点:
中介者承担了较多的责任,一旦中介者出现了问题,整个系统就会受到影响
如果设计不当,中介者对象本身变得过于复杂适用场合: 一组对象之间的通信方式比较复杂,导致相互依赖,结构混乱
一个对象引用很多其他对象并直接与这些对象通信,导致难以复用该对象中介者模式与外观模式:中介者模式注重的是内在的状态变化,而外观模式是注重的是外在的变化。
中介者模式和观察者模式:两者看起来很相似,都是通过注册然后去实现相应的行为,但是观察者模式实现的行为是固定的,而中介者模式需要实现的操作往往是需要变化的。
—————————————————————————————————————————————
二十二、备忘录模式
游戏进度保存:对象状态,场景…
想想有哪些方式:
SharedPreferences、Sqlite、Server
但是在一个大型项目往往是多人一起开发,如果每个人都用各自的保存方式,在维护方面是很困难的。
思考如何设计
首先我们必须考虑到多人开发必须得有相同的状态看管者,并且每个开发人员之间需要保存的状态互相之间是不知道,是安全的,因此我么可以分析出下图:
首先Originator和Originator2是两个状态发起人,MementoCaretaker是状态看管者,通过saveMemento()保存两个状态发起人通过createMemento创建的MementoIF,而MementoIF是一个空接口,为什么设计成一个空接口呢,这样在每个Originator通过createMemento的MenentoIF是相互独立,互相之间都是没有任何关系的,互相之间都是不了解的。保存在MementoCaretaker中的MenentoIF通过retrieveMemento取出来,然后发起来如果需要恢复之前的状态通过restoreMemento()传入从MementoCaretaker取出的MenentoIF强转成真实的MenentoIF通过调用自己的方法得到状态。
看一个完整的备忘录模式的图例:、
客户端通过对Originator操作,Originator通过对自己实现了空接口的MementorIF的私有Mementor进行操作,Caretaker通过对空接口MementorIF进行操作,并不能对真实的Mementor进行操作,保证了数据的安全,开发的高内聚。
备忘录模式:在不破坏封装的前提下,存储关键对象的重要状态,从而可以在将来把对象还原到存储的那个状态
下面看看代码:
public interface MementoIF {}
public class MementoCaretaker { private HashMap<String, MementoIF> mementomap; public MementoCaretaker() { mementomap = new HashMap<String, MementoIF>(); } public MementoIF retrieveMemento(String name) { return mementomap.get(name); } /** * 备忘录赋值方法 */ public void saveMemento(String name, MementoIF memento) { this.mementomap.put(name, memento); }}
public class Originator { private HashMap<String, String> state; public Originator() { state = new HashMap(); } public MementoIF createMemento() { return new Memento(state); } public void restoreMemento(MementoIF memento) { state = ((Memento) memento).getState(); } public void showState() { System.out.println("now state:" + state.toString()); } public void testState1() { state.put("blood", "500"); state.put("progress", "gate1 end"); state.put("enemy", "5"); } public void testState2() { state.put("blood", "450"); state.put("progress", "gate3 start"); state.put("enemy", "3"); } private class Memento implements MementoIF { private HashMap<String, String> state; private Memento(HashMap state) { this.state = new HashMap(state); } private HashMap getState() { return state; } private void setState(HashMap state) { this.state = state; } }}
public class Originator2 { private ArrayList<String> state; public Originator2() { state = new ArrayList<String>(); } public MementoIF createMemento() { return new Memento(state); } public void restoreMemento(MementoIF memento) { state = ((Memento) memento).getState(); } public void testState1() { state = new ArrayList<String>(); state.add("blood:320"); state.add("progress:gate2 mid"); state.add("enemy:15"); } public void testState2() { state = new ArrayList<String>(); state.add("blood:230"); state.add("progress:gate8 last"); state.add("enemy:12"); } public void showState() { System.out.println("now state:" + state.toString()); } private class Memento implements MementoIF { private ArrayList<String> state; private Memento(ArrayList<String> state) { this.state = new ArrayList(state); } private ArrayList<String> getState() { return state; } private void setState(ArrayList<String> state) { this.state = state; } }}
分析代码:大致流程还是和之前的图例一样,在MementoCaretaker 中通过传入一个key保存和取出MementoIF ,在每个Originator中都私有化一个实现了空接口MementoIF 的类,让Originator之间互相不能状态通信。在createMemento中通过new Memento(state),而在Memento中我们不是直接this.state=state,这样的形式是将一个引用赋给Memnto中的state,他们是同一块地址,做不到保存的效果,所以我们都重新new一个地址保存state,而在恢复状态通过restoreMemento参数强转成真实的Memento,通过getState()方法得到状态,为了演示两个Originator的通过两个 testState演示两个状态,通过showState演示状态。
下面看看测试代码:
public class MainTest { public static void main(String[] args) { MementoCaretaker mMementoCaretaker = new MementoCaretaker(); Originator mOriginator = new Originator(); Originator2 mOriginator2 = new Originator2(); System.out.println("*****Originator*****"); mOriginator.testState1(); mMementoCaretaker .saveMemento("Originator", mOriginator.createMemento()); mOriginator.showState(); mOriginator.testState2(); mOriginator.showState(); mOriginator.restoreMemento(mMementoCaretaker .retrieveMemento("Originator")); mOriginator.showState(); System.out.println("*****Originator 2*****"); mOriginator2.testState1(); mOriginator2.showState(); mMementoCaretaker.saveMemento("Originator2", mOriginator2.createMemento()); mOriginator2.testState2(); mOriginator2.showState(); mOriginator2.restoreMemento(mMementoCaretaker .retrieveMemento("Originator2")); mOriginator2.showState(); //System.out.println("*****Originator&&Originator 2*****"); // mOriginator.restoreMemento(mMementoCaretaker // .retrieveMemento("Originator2")); // mOriginator.showState(); }}
代码分析:首先假设玩家1 mOriginator在一个状态testState1玩,使用mMementoCaretaker保存玩家1 mOriginator的mOriginator.createMemento()对象,showState 显示现在的状态,然后玩家1 mOriginator开始在另一个状态testState2玩,showState 显示现在的状态,玩家1 mOriginator发现在testState2玩的不好,想回到testState1,mOriginator 玩家1 通过restoreMemento 得到在mMementoCaretaker保存的MementoIF,最后在通过showState 显示现在的状态
玩家2 类似,就不再啰嗦,
先看看效果:
从图中打印可以看到确实回到了之前的状态。
之前我们在MementoCaretaker 是通过key存储的MementoIF,所以在玩家一也可以用玩家二保存的key取出MementoIF,然后玩家一通过restoreMemento传入取出的MementoIF去恢复状态,我们测试代码注释部分就是这样操作的,但是这样是错误的,在玩家一传入玩家二保存的MementoIF时,由于玩家二的MementoIF是完全私有的,独立的,玩家一是根本获取不到的,所以也就无法拿到玩家二的状态,所以这里再次体现了我们空接口MementoIF的好处,保证了数据额独立,安全。
备忘录模式:在不破坏封装的前提下,存储关键对象的重要状态,从而可以在将来把对象还原到存储的那个状态
优点:
状态存储在外面,不和关键对象混在一起,这可以帮助维护内聚
提供了容易实现的恢复能力
保持了关键对象的数据封装
缺点:
资源消耗上面备忘录对象会很昂贵
存储和恢复状态的过程比较耗时注意事项: 注意开销
适用场合:
必须保存一个对象在某一个时刻的(整体或部分)状态,在对象以外的地方, 以后需要时恢复到先前的状态时
—————————————————————————————————————————————
二十三、原型模式
项目需求:银行每个月底通过Email发送活动账单给用户
特点:量大、时间要求紧
首先每封Email的标题和活动内容是相同的,只是具体的Email接收者,以及具体的内容格式是不同的,而且每封Email的格式肯定是相同的,这样我们可以将相同的部分抽取成一个类,不同的部分抽取出来,将相同的部分拼接按格式和不同的部分拼接在具体位置
下面看代码:
public class EventTemplate { private String eventSubject, eventContent; public EventTemplate(String eventSubject, String eventContent) { this.eventSubject = eventSubject; this.eventContent = eventContent; } public String geteventSubject() { return eventSubject; } public String geteventContent() { return eventContent; }}
subject和eventContent是相同的,分别代表标题和事件内容
public class Mail { private String receiver; private String subject; private String content; private String tail; public Mail(EventTemplate et) { this.tail = et.geteventContent(); this.subject = et.geteventSubject(); } public String getReceiver() { return receiver; } public void setReceiver(String receiver) { this.receiver = receiver; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } public void setContent(String content) { this.content = content; } public String getContent() { return content; } public String getTail() { return tail; } public void setTail(String tail) { this.tail = tail; }}
通过抽取的EventTemplate 类得到相同的部分,将邮件内容都封装在Mail类中。
public class MainTest { public static void main(String[] args) { int i = 0; int MAX_COUNT = 10; EventTemplate et = new EventTemplate("9月份信用卡账单", "国庆抽奖活动..."); Mail mail = new Mail(et); while (i < MAX_COUNT) { // 以下是每封邮件不同的地方 mail.setContent(getRandString(5) + ",先生(女士):你的信用卡账单..." + mail.getTail()); mail.setReceiver(getRandString(5) + "@" + getRandString(8) + ".com"); // 然后发送邮件 sendMail(mail); i++; } } public static String getRandString(int maxLength) { String source = "abcdefghijklmnopqrskuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; StringBuffer sb = new StringBuffer(); Random rand = new Random(); for (int i = 0; i < maxLength; i++) { sb.append(source.charAt(rand.nextInt(source.length()))); } return sb.toString(); } public static void sendMail(Mail mail) { System.out.println("标题:" + mail.getSubject() + "\t收件人:" + mail.getReceiver() + "\t内容:" + mail.getContent() + "\t....发送成功!"); }}
测试代码中通过getRandString得到随机的字母模拟用户的名字,通过sendMail简单额将Mail的内容打印,在main函数弘首先我们定义好相同部分EventTemplate ,传入Mail类,Mail设置好不同的部分,通过循环10次模拟发送10封邮件。
结果:
这种设计有啥问题?假如有500W邮件,每封发送时间0.1,500w*0.1s=50ws,时间消费太多了
这个时候有人肯定想到多线程sendMail,但是在sendMail时我们传入的Mail只有一个,也就是new了一次,在多线程中其中一个线程修改了Mail信息,可能会影响到下一个线程需要sendMail的具体内容,这是不安全,有人说将new Mail放在循环中,这样话Mail将会产生500W个地址,内存消耗是特别大的。
原型模式:通过复制现有实例来创建新的实例,无须知道相应类的信息
上图就是一个原型模式示例图,其实就是很简单的将new换成了clone,得到Mail的副本,而并非new出来额Mail对象。
下面看具体代码:
public class Mail implements Cloneable { private String receiver; private String subject; private String content; private String tail;// private ArrayList<String> ars; public Mail(EventTemplate et) { this.tail = et.geteventContent(); this.subject = et.geteventSubject(); } @Override public Mail clone() { Mail mail = null; try { mail = (Mail) super.clone();// mail.ars = (ArrayList<String>)this.ars.clone(); } catch (CloneNotSupportedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return mail; } public String getReceiver() { return receiver; } public void setReceiver(String receiver) { this.receiver = receiver; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } public void setContent(String content) { this.content = content; } public String getContent() { return content; } public String getTail() { return tail; } public void setTail(String tail) { this.tail = tail; }}
可以看到很简单,实现Cloneable 接口,实现clone接口。按照惯例,返回的对象应该通过调用 super.clone 获得
Object类的clone方法只会拷贝对象中的基本的数据类型,对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝。如果要实现深拷贝,必须将原型模式中的数组、容器对象、引用对象等另行拷贝。
我们看到上面代码注释的地方,ArrayList为clone不会拷贝的对象,所以我们必须通过mail.ars = (ArrayList)this.ars.clone() 进行拷贝
原型模式:通过复制现有实例来创建新的实例,无须知道相应类的信息
优点:
使用原型模式创建对象比直接new一个对象更有效
隐藏制造新实例的复杂性
重复地创建相似对象时可以考虑使用原型模式
缺点:
每一个类必须配备一个克隆方法
深层复制比较复杂注意事项: 使用原型模式复制对象不会调用类的构造方法。所以,单例模式与原型模式是冲突的,在使用时要特别注意。
适用场合:
复制对象的结构与数据
希望对目标对象的修改不影响既有的原型对象
创建对象成本较大的情况下
—————————————————————————————————————————————
二十四、访问者模式
项目需求:雇员管理系统。雇员的属性包括名字,收入,级别,度假天数等。雇员管理系统需要管理雇员的属性信息,并且需要管理每个雇员的补偿。
首先使用传统的设计我们很快可以写出如下代码:
public class Employee { private String name; private float income; private int vacationDays; private int degree; public Employee(String name, float income, int vacationDays, int degree) { this.name = name; this.income = income; this.vacationDays = vacationDays; this.degree = degree; } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setIncome(float income) { this.income = income; } public float getIncome() { return income; } public void setVacationDays(int vacationDays) { this.vacationDays = vacationDays; } public int getVacationDays() { return vacationDays; } public void setDegree(int degree) { this.degree = degree; } public int getDegree() { return degree; }}
public class Employees { private HashMap<String, Employee> employees; public Employees() { employees = new HashMap<String, Employee>(); } public void Attach(Employee employee) { employees.put(employee.getName(), employee); } public void Detach(Employee employee) { employees.remove(employee); } public Employee getEmployee(String name) { return employees.get(name); } public void getCompensation() { for (Employee employee : employees.values()) { System.out .println(employee.getName() + "'s Compensation is " + (employee.getDegree() * employee.getVacationDays() * 100)); } }}
Employee 为雇员类,其中包括需要包含的属性,Employees 为管理系统,通过HashMap管理各个Employee ,并通过遍历HashMap 通过getCompensation()方法管理补偿信息,根据等级getDegree*度假天数getVacationDays*10= 得到补偿的金额。
下面测试下:
public class MainTest { public static void main(String[] args) { Employees mEmployees = new Employees(); mEmployees.Attach(new Employee("Tom", 4500, 8, 1)); mEmployees.Attach(new Employee("Jerry", 6500, 10, 2)); mEmployees.Attach(new Employee("Jack", 9600, 12, 3)); mEmployees.getCompensation(); }}
现在补偿给每个员工的方式不使用原来的根据等级*度假天数*10得到补偿的金额,那么我们现在需要在管理系统类Employees 修改getCompensation()但是这破坏了开闭原则。其实我也想到了可以将Employee 类抽象,增加一个getCompensation()的抽象方法,每个雇员通过继承自抽象的Employee类去实现自己对应的补偿方式,然后Employees 管理系统只要对应的调用Employee 的getCompensation方法即可,但是我发现通过继承,每次雇员类都需要将name,income等等属性都重新继承,而他们变化的却只有补偿的方式,但是访问者模式就很好的解决了这个问题。
那么好吧,我们现在看看访问者模式设计这个项目的图例:
通过加入一个接口访问者类Visitor,并提供一个抽象的visit()方法传入需要访问的雇员类,这样就能知道具体在访问哪个雇员,在具体的ConcreteVisitor访问者类中实现visit()方法,通过visit()传入的参数去管理雇员的补偿信息,而在雇员类Employee 中添加一个Accept方法,传入Visitor作为参数,这样雇员类就能知道具体是哪个Visitor访问了自己,这样实现了双重指定,在雇员类中知道自己的访问者是谁,访问者中知道自己需要访问的雇员类是谁。
假如现在需要新的补偿类,我们需要重新实现Visitor类,得到具体的Visitor类,然后传入Employee,这样Employee就可以拿到新的补偿方式,这样我们就遵守了开闭原则。
下面看一张完整的访问者模式图例:
在上图例中,访问者被访问者都被抽取成接口,这样有利于扩展,无论你是雇员还是前雇员还是什么,只要实现了Element类,都可以拿到自己的访问者,而访问者中无论自己是什么方式的访问者只要实现了Visitor都可以得到被访问者。使用了双重指定。
访问者模式:对于一组对象,在不改变数据结构的前提下,增加作用于这些结构元素新的功能。
适用于数据结构相对稳定,它把数据结构和作用于其上的操作解耦,使得操作集合可以相对自由地演化
下面看看具体的代码:
public interface Visitor { abstract public void Visit( Element element );}
public class CompensationVisitor implements Visitor { @Override public void Visit(Element element) { // TODO Auto-generated method stub Employee employee = ((Employee) element); System.out.println(employee.getName() + "'s Compensation is " + (employee.getDegree() * employee.getVacationDays() * 10)); }}
public abstract class Element { abstract public void Accept(Visitor visitor);}
public class Employee extends Element { private String name; private float income; private int vacationDays; private int degree; public Employee(String name, float income, int vacationDays, int degree) { this.name = name; this.income = income; this.vacationDays = vacationDays; this.degree = degree; } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setIncome(float income) { this.income = income; } public float getIncome() { return income; } public void setVacationDays(int vacationDays) { this.vacationDays = vacationDays; } public int getVacationDays() { return vacationDays; } public void setDegree(int degree) { this.degree = degree; } public int getDegree() { return degree; } @Override public void Accept(Visitor visitor) { // TODO Auto-generated method stub visitor.Visit(this); }}
Element 抽取出来之后,这样就不只单单可以管理雇员类,但凡实现了Element Visitor 就能进行对它访问,而对于Visitor 只要实现了Visitor ,Element 就能得到自己的补偿方式。
public class Employees { private HashMap<String, Employee> employees; public Employees() { employees = new HashMap(); } public void Attach(Employee employee) { employees.put(employee.getName(), employee); } public void Detach(Employee employee) { employees.remove(employee); } public Employee getEmployee(String name) { return employees.get(name); } public void Accept(Visitor visitor) { for (Employee e : employees.values()) { e.Accept(visitor); } }}
管理系统类Employees 增加了Accept,通过传入Visitor ,遍历employees,将传入的Visitor 在传入每个Employee ,Employee 通过Accept(),然后通过传入的visitor的Visit去最终得到Employee 的补偿信息。
之前我也想过为了节省代码,在Element 中只需要定一个一个空接口,每个Element 无需实现Accept拿到访问者,在管理系统类中拿到Employee 后,直接通过visitor.Visit(Employee );的形式,也就是像这样:
public void Accept(Visitor visitor) { for (Employee e : employees.values()) { visitor.Visit(e); } }}
这样可以节省代码,但是我后来想想在Employee 中就不知道哪个visitor访问了它,这样就不符合实际意义。
public class MainTest { public static void main(String[] args) { Employees mEmployees = new Employees(); mEmployees.Attach(new Employee("Tom", 4500, 8, 1)); mEmployees.Attach(new Employee("Jerry", 6500, 10, 2)); mEmployees.Attach(new Employee("Jack", 9600, 12, 3)); mEmployees.Accept(new CompensationVisitor()); }}
通过指定额访问者得到补偿结果。假如需要改变补偿方式,只需要重新实现Visitor(),传入新的访问者。结果和之前也是一样的。但是现在通过访问者有利于扩展,并且遵守开闭原则。
访问者模式:对于一组对象,在不改变数据结构的前提下,增加作用于这些结构元素新的功能。
优点:
符合单一职责原则
扩展性良好
有益于系统的管理和维护
缺点:
增加新的元素类变得很困难
破坏封装性注意事项: 系统有比较稳定的数据结构
与迭代器的关系。在迭代器模式中我们也是遍历许多对象,但是在迭代器模式中我们并不需知道每个对象需要具体的操作。适用场合: 如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的
—————————————————————————————————————————————
二十五、设计模式总结
一、什么是设计模式
模式:在某些场景下,针对某类问题的某种通用解决方案
场景:项目环境
问题:约束条件,项目目标等
解决方案:通用、可以复用的设计,解决约束,达到目标
二、设计模式的三个分类
2.1 创建型模式:对象实例化的模式,创建型模式解耦了对象的实例化过程
简单工厂:一个工厂类根据传入的参量决定创建出哪一种产品类的实例
工厂方法:定义一个创建对象的接口,让子类决定实例化哪一个类
抽象工厂:创建相关或依赖对象的家族,而无需明确指定具体类
单例模式:某个类只能有一个实例,提供一个全局访问点
生成器模式:封装一个复杂对象的构建过程,并可以按步骤构造
原型模式:通过复制现有的实例来创建新的实例
2.2 结构型模式:把类或对象结合在一起形成更大的结构
适配器模式:将一个类的方法接口转换成客户希望的另外一个接口
组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构
装饰模式:动态地给对象添加新的功能
代理模式:为其他对象提供一个代理以控制对这个对象的访问
蝇量模式:通过共享技术有效地支持大量细粒度的对象
外观模式:提供统一的方法来访问子系统的一群接口
桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化
2.3 行为型模式:类和对象如何交互,及划分责任和算法
模板模式:定义一个算法结构,而将一些步骤延迟到子类中实现
解释器模式:给定一个语言, 定义它的文法的一种表示,并定义一个解释器
策略模式:定义一系列的算法,把它们封装起来, 并且使它们可相互替换
状态模式:允许一个对象在其内部状态改变时改变它的行为
观测者模式:对象间的一对多的依赖关系
备忘录模式:在不破坏封装性的前提下,保存对象的内部状态
中介者模式:用一个中介对象来封装一系列的对象交互
命令模式:将命令请求封装为一个对象,使得可用不同的请求来进行参数化
访问者模式:在不改变数据结构的前提下,增加作用于一组对象元素新的功能
责任链:请求发送者和接收者之间解耦,使的多个对象都有机会处理这个请求
迭代器:一种遍历访问聚合对象中各个元素的方法,不暴露该对象的内部结构
三、设计模式的六大原则
3.1 组合复用原则
多用组合,少用继承
找到变化部分,抽象,封装变化
区分“Has-A”与“Is-A”如果HasA表示你有它应该使用组合,IsA表示你是它应该使用继承。
3.2 依赖倒置原则
依赖:成员变量、方法参数、返回值
要依赖于抽象,不要依赖于具体
高层模块不应该依赖低层模块,二者都应该依赖其抽象
抽象不应该依赖具体,具体应该依赖抽象
针对接口编程,不要针对实现编程以抽象为基础搭建的结构比具体类搭建的结构要稳定的多
在java中,抽象指的是接口或者抽象类,具体就是具体的实现类
3.3 开闭原则
对扩展开放,对修改关闭
通过扩展已有软件系统,可以提供新的功能
修改的关闭,保证稳定性和延续性
3.4 迪米特法则
一个对象应该与其他对象保持最少的了解。只与直接朋友交谈。
成员变量、方法参数、方法返回值中需要的类为直接朋友
类与类之间的关系越密切了解越多,耦合度越大
尽量降低类与类之间的耦合
外观模式、中介者模式接口隔离原则:一个类对另一个类的依赖应该建立在最小的接口上
3.5 里氏替换原则
所有引用基类的地方必须能透明地使用其子类对象
子类在扩展父类功能时不能破坏父类原有的功能
使用继承时,遵循里氏替换原则:
子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
当子类重载父类方法时,方法的形参要比父类方法的参数更宽松
当子类实现父类的抽象方法时,方法的返回值要比父类更严格里氏替换原则是设计整个继承体系的原则
3.6 单一职责原则
类应该只有一个导致类变更的理由
即一个类只负责一项职责降低类的复杂度
提高系统的可维护性
修改时降低风险溢出
四、用模式来思考
4.1 保持简单
尽可能用最简单的方式解决问题
简单而弹性的设计,一般使用模式是最好的方法
4.2 设计模式非万能
模式是通用问题的经验总结
使用模式时要考虑它对其他部分的影响
不需要预留任何弹性的时候,删除掉模式
平衡与妥协
4.3 何时需要模式
找出设计中会变化的部分,通常就是需要考虑模式的地方
重构时
4.4 重构的时间就是模式的时间
重构就是改变代码来改进组织方式的过程
利用模式来重构
源码下载
- Java23种设计模式
- JAVA23种设计模式
- JAVA23种设计模式
- JAVA23种设计模式
- JAVA23种设计模式
- JAVA23种设计模式
- JAVA23种设计模式
- java23种设计模式
- Java23种设计模式
- java23种设计模式
- java23种设计模式
- java23种设计模式
- java23种设计模式
- java23种设计模式
- Java23种设计模式
- java23种设计模式
- java23种设计模式
- java23种设计模式
- JavaScript 调用jQuery 时 页面toTop的写法
- 第一章 JavaScript简介
- 相册dialog
- react-native ndk版本下载
- 学习设计模式的一点心得
- Java23种设计模式
- 有意思的基础题一
- php面试题之六——Linux部分(高级部分)
- 用Eclipse将写好的项目打包成jar包,后续再打成exe程序
- C# 获取版本号、资源文件、全局配置Settings.Settings
- MapReduce工作原理与开发实践
- 51单片机定时器的原理与使用(二)
- JavaScript Window-浏览器对象模型
- android 4行代码解析短信内容(全手动)