Patterns in Java
23個模式,被分類爲創建型模式(Creational Patterns),結構型模式(Structural Patterns)和行爲模式(Behavioral Patterns)
1. 創建模式
1.Singleton(單列模式)
基本概念
Singleton 是一種創建性模型,它用來確保只產生一個實例,並提供一個訪問它的全局訪問點.對一些類來說,保證只有一個實例是很重要的,比如有的時候,數據庫連接或 Socket 連接要受到一定的限制,必須保持同一時間只能有一個連接的存在.再舉個例子,集合中的 set 中不能包含重複的元素,添加到set裏的對象必須是唯一的,如果重複的值添加到 set,它只接受一個實例.JDK中正式運用了Singleton模式來實現 set 的這一特性,大家可以查看java.util.Collections裏的內部靜態類SingletonSet的原代碼.其實Singleton是最簡單但也是應用最廣泛的模式之一,在 JDK 中隨處可見。
簡單分析
爲了實現 Singleton 模式,我們需要的是一個靜態的變量,能夠在不創建對象的情況下記憶是否已經產生過實例了.靜態變量或靜態方法都可以在不產生具體實例的情況下直接調用, 這樣的變量或方法不會因爲類的實例化而有所改變。
具體實現
1. 靜態方法實現
1.1. 直觀式
publicclass Singleton {
privatestatic Singleton instance = new Singleton();
private Singleton(){}
//在自己內部定義自己一個實例,是不是很奇怪?
//注意這是private 只供內部調用
//這裏提供了一個供外部訪問本class的靜態方法,可以直接訪問
publicstatic Singleton getInstance() {
return instance;
}
}
|
或者
Publicclass Singleton {
Privatestatic Singleton instance;
Static{
try {
instance = new MyFactory();
} catch (IOException e) {
throw new RuntimeException("Darn, an error's occurred!", e);
}
}
private Singleton(){}
publicstatic Singleton getInstance() {
return instance;
}
}
1.2. Lazy initialization
publicclass Singleton {
privatestatic Singleton instance = null;
publicstaticsynchronized Singleton getInstance() {
//這個方法比上面有所改進,不用每次都進行生成對象,只是第一次
//使用時生成實例!
if (instance==null)
instance=new Singleton();
return instance;
}
}
|
2. 靜態變量爲標誌實現
class SingletonException extends RuntimeException {
public SingletonException(String s) {
super(s);
}
}
Public class Singleton {
static boolean instance_flag = false; // true if 1 instance
public Singleton() {
if (instance_flag)
throw new SingletonException("Only one instance allowed");
else
instance_flag = true; // set flag for 1 instance
}
}
|
3. 註冊器機制實現
首先用集合中的Hashtable 和Enumeration來實現addItem(Object key, Object value),getItem(Object key), ,removeItem(Object key)等方法實現一個管理器,將key和value一一關聯起來,客戶程序員創建實例前首先用addItem方法進行註冊,再用getItem方法獲取實例.Hashtable中的key是唯一的,從而保證創建的實例是唯一的,具體實現限於篇幅不再細說,在Prototype模型的應用一文中我將會給出一個實現註冊器的代碼.用註冊器機制來創建 Singleton模式的好處是易於管理,可以同時控制多個不同類型的Singleton 實例。
注意事項
Singleton模式看起來簡單,使用方法也很方便,但是真正用好,是非常不容易,需要對Java的類,線程,內存等概念有相當的瞭解。如果你的應用基於容器,那麼Singleton模式少用或者不用,可以使用相關替代技術。
有時在某些情況下,使用Singleton並不能達到Singleton的目的,如有多個Singleton對象同時被不同的類裝入器裝載;在EJB這樣的分佈式系統中使用也要注意這種情況,因爲EJB是跨服務器,跨JVM的。
注意到在靜態方法實現singleton時,lazy initialization形式中的synchronized,這個synchronized很重要,如果沒有synchronized,那麼使用getInstance()是有可能得到多個Singleton實例。一般認爲第一種形式要更加安全些。關於lazy initialization的Singleton有很多涉及double-checked locking (DCL) 同步開銷,如下:
public class MyFactory {
private static MyFactory instance;
Public synchronized static MyFactory getFactory() {
if (instance == null)
instance = new MyFactory();
return instance;
}
}
|
上面的例子是完全正確的,考慮到所有的Read操作也需要同步,爲了避免昂貴的同步開銷
public class MyBrokenFactory {
private static MyFactory instance;
private int field1, field2 ;
public static MyBrokenFactory getFactory() {
if (instance == null) {
synchronized (MyBrokenFactory.class)
{
instance = new MyFactory();
}
}
return instance;
}
private MyBrokenFactory() {
field1 = ...;
field2 = ...;
}
}
|
上面的做法是不正確的,考慮2個線程同時調用MyBrokenFactory.getFactory(),線程2在線程1完成對象創建到該對象初始化之間就可能得到了對象的引用(Thread 2 gets in just when Thread 1 has written the object reference to memory, but before it has written all the fields.)。可以採用靜態方法直接式來避免這個問題,但是要不是去lazy策略,可以採用javaEE5 方式:
public class MyFactory {
private static volatile MyFactory instance;
public static MyFactory getInstance(Connection conn) throws IOException { if (instance == null) { //first check synchronized (MyFactory.class) { if (instance == null) // second check instance = new MyFactory(conn);
}
}
return instance; }
private MyFactory(Connection conn) throws IOException { // init factory using the database connection passed in
}
}
JAVA5以後,訪問一個volatile的變量具有synchronized 的語義。換句話說,JAVA5保證unsycnrhonized volatile read 會在寫之後發生。
|
2.Prototype(原型模式)
基本概念
用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象.
簡單分析
Prototype模式允許一個對象再創建另外一個可定製的對象,根本無需知道任何如何創建的細節, 工作原理:通過將一個原型對象傳給那個要發動創建的對象,這個要發動創建的對象通過請求原型對象拷貝它們自己來實施創建。
具體實現
public abstract class AbstractSpoon implements Cloneable { String spoonName; public void setSpoonName(String spoonName) {this.spoonName = spoonName;} public String getSpoonName() {return this.spoonName;} public Object clone() { Object object = null; try { object = super.clone(); } catch (CloneNotSupportedException exception) { System.err.println("AbstractSpoon is not Cloneable"); } return object; } } |
public class SoupSpoon extends AbstractSpoon { public SoupSpoon() { setSpoonName("Soup Spoon"); } } |
調用Prototype模式很簡單:
AbstractSpoon spoon = new SoupSpoon();
AbstractSpoon spoon2 = spoon.clone();
在Java中Prototype模式變成clone()方法的使用,由於Java的純潔的面向對象特性,使得在Java中使用設計模式變得很自然,兩者已經幾乎是渾然一體了。
3. Factory Method(工廠方法模式)-----工廠模式1
基本概念:
提供創建對象的接口
簡單分析:
工廠模式相當於創建對象的new,考慮用它主要是考慮爲你的系統帶來更大的可擴展性和儘量少的修改量。假如在創建對象時,需要初始化許多工作,當然可以在構造函數中通過傳值的方式來實現,但是這樣的話,你的構造函數會非常臃腫,不宜於閱讀,還有初始化工作複雜,如果在同一個方法中,勢必會帶來危險,不要將很多雞蛋放在一個籃子裏面,這也有悖於java面向對象的原則(封裝)。
具體實現
Public class MethodFactory {
Public static Sample creator(int flag)
{
If(flag ==1) return new SampleA();
Else if (flag ==2) return new SampleB();
//…這裏可能更適合使用class.forName(),通過反射機制來創建對象
}
}
|
Sample |
SampleA |
SampleB |
Factory |
GetClass |
Param |
Sample |
4. Abstract Factory(抽象工廠模式)-----工廠模式2
基本概念:
基於工廠方法,如果創建對象的過程更復雜。
簡單分析:
如上述的Sample 下有SampleA, SampleB 產品,如果在需要此上 還有Demo 接口下還有DemoA , DemoB產品,並且同中接口下的不同產品或許存在某種特定關係,那應該就考慮到用抽象工廠了。那我們把相同部分封裝在抽象工廠了,那不同部分就在具體的工廠裏面實現。
具體實現:
Public abstract class Factory{
Public abstract Sample createSample ();
Public abstract Demo createDemo ();
}
|
Public class SampleFactory extends Factory{
Public abstract Sample createSample(){
Return new SampleA();
}
Public abstract Demo createDemo(){
Return new DemoA();
}
}
|
Public class DemoFactory extends Factory{
Public abstract Sample createSample(){
Return new SampleB();
}
Public abstract Demo createDemo(){
Return new DemoB();
}
}
|
使用:
Factory samplefactory =new SampleFactory();
Sample sampleA = Samplefactory. createSample();
Demo demoA = Samplefactory. createDemo();
Factory demofactory =new DemoFactory();
Sample sampleB = demofactory. createSample();
Demo demoB = demofactory. createDemo();
|
SampleB |
Sample |
SampleA |
Factory |
GetClass |
Param |
Sample |
Demo |
DemoA |
DemoB |
SampleFactory |
DemoFactory |
Demo |
5. Builder(建造模式)
基本概念:
將一個複雜的對象的構建與他的表示分離,使得同樣的構建過程可以創建不同的表示。
簡單分析:
此模式是一步步創建一個複雜的對象,它允許用戶可以通過指定複雜對象的類型和內容就可以構建他們,擁護不知道內部的具體實現細節,非常類似抽象工廠模式。但是有細微差別。爲了將構建對象的複雜過程和他的部件解藕。
具體實現:
比如在汽車的裝配過程,需要將許許多多的部件創建,然後組裝。
1.構建的具體過程抽象
Public interface Builder {
Void buildPartA(); //創建部件A
Void buildPartB(); //創建部件B
Void buildPartC(); //創建部件C
Void buildPartD(); //創建部件D
Product getProduct(); //組裝汽車。
}
|
2.組裝過程抽象
Public class Director{
Private Builder builder;
Public Director(Builder builder)
{
This. Builder = builder;
}
Public void construct()
{
builder.buildPartA(); //創建部件A
builder.buildPartB(); //創建部件B
builder.buildPartC(); //創建部件C
builder.buildPartD(); //創建部件D
}
}
|
3.構建的具體實現
Public class ConreteBuilder implements Builder{
Private Product product;
Private Part A,B,C,D;
Void buildPartA(){this .A = new A();}
Void buildPartB(){this .B = new B();}
Void buildPartC(){this .C = new C();}
Void buildPartD(){this .D = new D();}
Product getProduct ()
{
This .product = new Product(A,B,C,D);
Return this.product;
}
}
|
4. 使用:
ConreteBuilder builder = new ConreteBuilder();
Director director = new Director(builder);
Director. Construct();
Product product = Builder. getProduct();
|
2. 結構型模式
1. Adapter (適配器模式)
基本概念:
將兩個不兼容的類糾合在一起使用,需要有Adaptee(被適配者)和Adaptor(適配器)兩個身份.
簡單分析:
我們經常碰到要將兩個沒有關係的類組合在一起使用,第一解決方案是:修改各自類的接口,但是如果我們沒有源代碼,或者,我們不願意爲了一個應用而修改各自的接口.使用Adapter,在這兩種接口之間創建一個混合接口(混血兒).
具體實現:
實現Adapter方式,其實"think in Java"的"類再生"一節中已經提到,有兩種方式:組合(composition)和繼承(inheritance).
假設我們要打樁,有兩種類:方形樁圓形樁.
public class SquarePeg
{
public void insert(String str){
System.out.println("SquarePeg insert():"+str);
}
}
public class RoundPeg{
public void insertIntohole(String msg){
System.out.println("RoundPeg insertIntoHole():"+msg);
}
}
|
1.組合繼承使用
public class PegAdapter extends SquarePeg{
private RoundPeg roundPeg;
public PegAdapter(RoundPeg peg)(this.roundPeg=peg;)
public void insert(String str){ roundPeg.insertIntoHole(str);}
}
|
2. 高級使用(實用於多繼承)
public interface IRoundPeg{
public void insertIntoHole(String msg);
}
public interface ISquarePeg{
public void insert(String str);
}
public class SquarePeg implements ISquarePeg{
public void insert(String str){
System.out.println("SquarePeg insert():"+str);
}
}
public class RoundPeg implements IRoundPeg{
public void insertIntohole(String msg){
System.out.println("RoundPeg insertIntoHole():"+msg);
}
}
public class PegAdapter implements IRoundPeg,ISquarePeg{
private RoundPeg roundPeg;
private SquarePeg squarePeg;
// 構造方法
public PegAdapter(RoundPeg peg)
{this.roundPeg=peg;}
// 構造方法 public PegAdapter(SquarePeg peg)
(this.squarePeg=peg;)
public void insert(String str)
{ roundPeg.insertIntoHole(str);}
}
|
2. Bridge (橋接模式)
基本概念:
將抽象和行爲劃分開來,各自獨立,但能動態的結合。
簡單分析:
任何事物對象都有抽象和行爲之分,例如人,人是一種抽象,人分男人和女人等;人有行爲,行爲也有各種具體表現,所以,“人”與“人的行爲”兩個概念也反映了抽象和行爲之分。
在面向對象設計的基本概念中,對象這個概念實際是由屬性和行爲兩個部分組成的,屬性我們可以認爲是一種靜止的,是一種抽象,一般情況下,行爲是包含在一個對象中,但是,在有的情況下,我們需要將這些行爲也進行歸類,形成一個總的行爲接口,這就是橋模式的用處。
應用場景:
Business Object >> Java DAO
具體實現:
這多個子類之間概念是並列的,如前面舉例,打樁,有兩個concrete class:方形樁和圓形樁;這兩個形狀上的樁是並列的,沒有概念上的重複。2.這多個子類之中有內容概念上重疊.那麼需要我們把抽象共同部分和行爲共同部分各自獨立開來,原來是準備放在一個接口裏,現在需要設計兩個接口:抽象接口和行爲接口,分別放置抽象和行爲. 例如,一杯咖啡爲例,子類實現類爲四個:中杯加奶、大杯加奶、中杯不加奶、大杯不加奶。
抽象接口:咖啡
public abstract class Coffee {
CoffeeAction coffeeAction;
public void setCoffeeAction () {
this.coffeeAction = CoffeeActionSingleton.getTheCoffeeAction ();
}
public CoffeeImp getCoffeeAction() {
return this. coffeeAction;
}
public abstract void pourCoffee();
}
|
行爲接口:附加動作
public abstract class CoffeeAction {
public abstract void pourCoffeeAction();
}
|
具體抽象接口實現類:大杯,中杯
Public MediumCoffee extends Coffee
{
Public MediumCoffee(){setCoffeeAction ()}
public void pourCoffee()
{
for(int i=0;i<2;i++)//2次爲小杯
coffeeAction.pourCoffeeAction();
}
}
Public BigCoffee extends Coffee
{
Public MediumCoffee(){setCoffeeAction ()}
public void pourCoffee()
{
for(int i=0;i<5;i++)//5次爲小杯
coffeeAction.pourCoffeeAction();
}
}
|
具體行爲接口實現類:加奶,不加奶。。。
//加奶
public class MilkCoffeeAction extends CoffeeAction {
MilkCoffeeAction () {}
public void pourCoffeeAction()
{
System.out.println("加了美味的牛奶");
}
}
//不加奶
public class FragrantCoffeeAction extends CoffeeAction {
FragrantCoffeeAction () {}
public void pourCoffeeAction()
{
System.out.println("什麼也沒加,清");
}
}
|
public class CoffeeImpSingleton
{
private static CoffeeImp coffeeImp;
public CoffeeImpSingleton(CoffeeImp coffeeImpIn)
{this.coffeeImp = coffeeImpIn;}
public static CoffeeImp getTheCoffeeImp()
{ return coffeeImp; }
}
|
//拿出牛奶
CoffeeImpSingleton coffeeImpSingleton = new CoffeeImpSingleton(new MilkCoffeeImp());
//中杯加奶
MediumCoffee mediumCoffee = new MediumCoffee(); mediumCoffee.pourCoffee();
//大杯加奶
SuperSizeCoffee superSizeCoffee = new SuperSizeCoffee(); superSizeCoffee.pourCoffee();
|
3. Composite (合成模式)
基本概念:
將對象以樹形結構組織起來,以達成“部分-整體” 的層次結構,使得客戶端對單個對象和組合對象的使用具有一致性.
簡單分析:
具體實現:
4. Decorator (門面模式,裝飾模式)
基本概念:
動態給一個對象添加一些額外的職責, 就象在牆上刷油漆.使用Decorator模式相比用生成子類方式達到功能的擴充顯得更爲靈活.
簡單分析:
我們通常可以使用繼承來實現功能的拓展,如果這些需要拓展的功能的種類很繁多,那麼勢必生成很多子類,增加系統的複雜性,同時,使用繼承實現功能拓展,我們必須可預見這些拓展功能,這些功能是編譯時就確定了,是靜態的.
使用Decorator的理由是:這些功能需要由用戶動態決定加入的方式和時機.Decorator提供了"即插即用"的方法,在運行期間決定何時增加何種功能.
應用場景:
Java 文件讀寫 I/O
FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr);
具體實現:
舉Adapter中的打樁示例,在Adapter中有兩種類:方形樁圓形樁,Adapter模式展示如何綜合使用這兩個類,在Decorator模式中,我們是要在打樁時增加一些額外功能,比如,挖坑,在樁上釘木板等,不關心如何使用兩個不相關的類.
//具體工作
public interface Work {
public void insert();
}
|
//打樁
public class SquarePeg implements Work{
public void insert(){
System.out.println("方形樁插入");
}
}
|
//裝飾
public class Decorator implements Work{
private Work work;
//額外增加的功能被打包在這個List中
private ArrayList others = new ArrayList();
//在構造器中使用組合new方式,引入Work對象;
public Decorator(Work work) {
this.work=work;
others.add("挖坑");
others.add("釘木板");
}
public void insert(){
newMethod();
}
//在新方法中,我們在insert之前增加其他方法,這裏次序先後是用戶靈活指定的
public void newMethod() {
otherMethod();
work.insert();
}
public void otherMethod() {
ListIterator listIterator = others.listIterator();
while (listIterator.hasNext()) {
System.out.println(((String)(listIterator.next()))
+ " 正在進行"); } }
}
}
|
使用:
Work squarePeg = new SquarePeg();
Work decorator = new Decorator(squarePeg);
decorator.insert();
|
5. Façade (外觀模式)
基本概念:
爲子系統中的一組接口提供一個一致的界面.
簡單分析:
典型應用就是數據庫JDBC的應用,
具體實現:
public class DBCompare {
Connection conn = null;
PreparedStatement prep = null;
ResultSet rset = null;
try {
Class.forName( "<driver>" ).newInstance();
conn = DriverManager.getConnection( "<database>" );
String sql = "SELECT * FROM <table> WHERE <column name> = ?";
prep = conn.prepareStatement( sql );
prep.setString( 1, "<column value>" );
rset = prep.executeQuery();
if( rset.next() ) {
System.out.println( rset.getString( "<column name" ) );
}
} catch( SException e ) {
e.printStackTrace();
} finally {
rset.close();
prep.close();
conn.close();
}
|
6. Flyweight (亨元模式)
基本概念:
避免擁有大量有相同內容的小類的開銷,使大家共享一個類(元類)
簡單分析:
面嚮對象語言的原則一切皆對象,但是有時對象的數量可能相當龐大,比如字處理軟件,幾千個字就是幾千的對象,這樣內存耗費相當巨大,那我們採用“求同存異”,找出共同點作爲一個對象,封裝可以被共享得類,即爲元類。Flyweight有兩個重要概念,外部狀態,內部狀態。
具體實現:
它常常會結合工廠模式一起來實現複雜的過程。
Public interface Flyweight{
Public void operator(ExstrinsicState extState);
}
|
Public interface ExstrinsicState {
//外部狀態,自行實現
}
|
Public Class ConcreteFlyweight extends Flyweight {
Private InstrinsicState instrState;
//內部狀態,共享數據,也可以沒有共享數據
Public void operator(ExstrinsicState extState){
//外部狀態,自行實現
}
}
|
Public Class FlyweightFactory {
private Hashtable flyweights = new Hashtable();
public Flyweight getFlyweight( Object key ) {
Flyweight flyweight = (Flyweight) flyweights.get(key);
if( flyweight == null ) {
//產生新的ConcreteFlyweight
flyweight = new ConcreteFlyweight();
flyweights.put( key, flyweight );
}
return flyweight;
}
}
|
使用
FlyweightFactory factory = new FlyweightFactory();
Flyweight fly1 = factory.getFlyweight( "Fred" );
Flyweight fly2 = factory.getFlyweight( "Wilma" );
|
7. Proxy (代理模式)
基本概念:
爲其他對象提供一種代理以控制對這個對象的訪問。
簡單分析:
代理模式是比較有用途的一種模式,而且變種較多,應用場合覆蓋從小結構到整個系統的大結構,Proxy是代理的意思,我們也許有代理服務器等概念,代理概念可以解釋爲:在出發點到目的地之間有一道中間層,意爲代理.
應用場景:
1.授權機制
2. 提高系統性能,特別是進行大文件處理,遠程網絡訪問
3.額外爲對象添加一些功能。
具體實現:
1. 代理授權
public class ForumProxy implements Forum {
private ForumPermissions permissions;
private Forum forum;
this.authorization = authorization;
public ForumProxy(Forum forum, Authorization authorization, ForumPermissions permissions)
{
this.forum = forum;
this.authorization = authorization;
this.permissions = permissions;
}
public void setName(String name) throws UnauthorizedException, ForumAlreadyExistsException {
//只有是系統或論壇管理者纔可以修改名稱
if (permissions.isSystemOrForumAdmin()) {
forum.setName(name);
} else
{
throw new UnauthorizedException();
}
}
|
2. 動態代理
|
3. 行爲型模式
1. Chain of responsibility
基本概念:
Chain of Responsibility(CoR) 是用一系列類(classes)試圖處理一個請求request
簡單分析:
Chain of Responsibility(CoR) 是用一系列類(classes)試圖處理一個請求request,這些類之間是一個鬆散的耦合,唯一共同點是在他們之間傳遞request. 也就是說,來了一個請求,A類先處理,如果沒有處理,就傳遞到B類處理,如果沒有處理,就傳遞到C類處理,就這樣象一個鏈條(chain)一樣傳遞下去。
具體實現:
public interface Handler
{
public void handleRequest(Request request);
}
|
public class Request{
private String type;
public Request(String type){this.type=type;}
public String getType(){return type;}
public void execute(){
//request真正具體行爲代碼
}
}
|
public class ConcreteHandler implements Handler
{
private Handler successor;
public ConcreteHandler(Handler successor)
{
this.successor=successor;
}
public void handleRequest(Request request){
if (request instanceof HelpRequest){
//這裏是處理Help的具體代碼
}
else if (request instanceof PrintRequst)
{
request.execute();
}else
//傳遞到下一個
successor.handle(request);
}
}
}
|
2. Command (命令模式)
基本概念:
將來自客戶端的請求傳入一個對象,無需瞭解這個請求激活的動作或有關接受這個請求的處理細節。
這是一種兩臺機器之間通訊聯繫性質的模式,類似傳統過程語言的 CallBack功能。
簡單分析:
解耦了發送者和接受者之間聯繫。 發送者調用一個操作,接受者接受請求執行相應的動作,因爲使用Command模式解耦,發送者無需知道接受者任何接口。
不少Command模式的代碼都是針對圖形界面的,它實際就是菜單命令,我們在一個下拉菜單選擇一個命令時,然後會執行一些動作.
將這些命令封裝成在一個類中,然後用戶(調用者)再對這個類進行操作,這就是Command模式,換句話說,本來用戶(調用者)是直接調用這些命令的,如菜單上打開文檔(調用者),就直接指向打開文檔的代碼,使用Command模式,就是在這兩者之間增加一箇中間者,將這種直接關係拗斷,同時兩者之間都隔離,基本沒有關係了.
顯然這樣做的好處是符合封裝的特性,降低耦合度,Command是將對行爲進行封裝的典型模式,Factory是將創建進行封裝的模式,
具體實現:
public interface Command {
public abstract void execute ( );
}
|
public class Engineer implements Command {
public void execute( ) { //do Engineer's command }
}
public class Programmer implements Command {
public void execute( ) { //do programmer's command }
}
public class Politician implements Command {
public void execute( ) { //do Politician's command }
}
|
public class producer{
public static List produceRequests() {
List queue = new ArrayList();
queue.add( new DomesticEngineer() );
queue.add( new Politician() );
queue.add( new Programmer() ); return queue;
}
}
|
public class TestCommand {
public static void main(String[] args) {
List queue = Producer.produceRequests();
for (Iterator it = queue.iterator(); it.hasNext(); )
//客戶端直接調用execute方法,無需知道被調用者的其它更多類的方法名。
(Command)it.next()).execute();
|
2. Iterator (迭代模式)
基本概念:
Iterator模式可以順序的訪問一個聚集中的元素而不必暴露聚集的內部情況。
簡單分析:
Iterator模式的優點是當(ConcreteTeacher)對象中有變化是,比如說同學出勤集合中有加入了新的同學,或減少同學時,這種改動對客戶端是沒有影響的。
具體實現:
public interface Iterator {
void first(); //第一個
void next(); //下一個
boolean isDone(); //是否點名完畢
Object currentItem(); //當前同學的出勤情況
}
|
一般都是使用匿名類來實現
public class ConcreteIterator implements Iterator{
private ConcreteTeacher teacher;
private int index = 0;
private int size = 0;
public ConcreteIterator(ConcreteTeacher teacher)
{
this.teacher = teacher;
size = teacher.getSize(); //得到同學的數目
index = 0;
}
public void first(){ //第一個
index = 0;
}
public void next(){ //下一個
if(index<size){
index++;
}
}
public boolean isDone(){ //是否點名完畢
return (index>=size);
}
public Object currentItem(){ //當前同學的出勤情況
return teacher.getElement(index);
}
}
|
public interface Teacher {
public Iterator createIterator(); //點名
}
|
public class ConcreteTeacher implements Teacher{
private Object[] present = {"張三來了","李四來了","王五沒來"};
//同學出勤集合
public Iterator createIterator(){
return new ConcreteIterator(this); //新的點名
}
public Object getElement(int index){ //得到當前同學的出勤情況
if(index<present.length){
return present[index];
}
else{
return null;
}
}
public int getSize(){
return present.length; //得到同學出勤集合的大小,也就是說要知道班上有多少人
}
}
|
測試:
public class Test {
private Iterator it;
private Teacher teacher = new ConcreteTeacher();
public void operation(){
it = teacher.createIterator(); //老師開始點名
while(!it.isDone()){ //如果沒點完
System.out.println(it.currentItem().toString()); //獲得被點到同學的情況
it.next(); //點下一個
}
}
public static void main(String agrs[]){
Test test = new Test();
test.operation();
}
}
|
4.Interpreter (翻譯模式)
基本概念:
定義語言的文法,並且建立一個解釋器來解釋該語言中的句子.
簡單分析:
Interpreter似乎使用面不是很廣,它描述了一個語言解釋器是如何構成的,在實際應用中,我們可能很少去構造一個語言的文法
具體實現:
public interface AbstractExpression {
void interpret( Context context );
}
// AbstractExpression的具體實現分兩種:終結符表達式和非終結符表達式: |
public interface AbstractExpression {
void interpret( Context context );
}
public interface Context {
}
public class NonterminalExpression implements AbstractExpression {
private AbstractExpression successor;
public void setSuccessor( AbstractExpression successor )
{ this.successor = successor; }
public AbstractExpression getSuccessor()
{return successor; }
public void interpret( Context context ) { } }
|
5.Mediator (中介者模式)
基本概念:
用一箇中介對象來封裝一系列關於對象交互行爲.
簡單分析:
各個對象之間的交互操作非常多;每個對象的行爲操作都依賴彼此對方,修改一個對象的行爲,同時會涉及到修改很多其他對象的行爲,如果使用Mediator模式,可以使各個對象間的耦合鬆散,只需關心和Mediator的關係,使多對多的關係變成了一對多的關係,可以降低系統的複雜性,提高可修改擴展性.
Mediator模式在事件驅動類應用中比較多,例如界面設計GUI.;聊天,消息傳遞等,在聊天應用中,需要有一個MessageMediator,專門負責request/reponse之間任務的調節.
MVC是J2EE的一個基本模式,View Controller是一種Mediator,它是Jsp和服務器上應用程序間的Mediator.
具體實現:
public interface Mediator { }
|
public class ConcreteMediator implements Mediator
{
//假設當前有兩個成員.
private ConcreteColleague1 colleague1 = new ConcreteColleague1(); private ConcreteColleague2 colleague2 = new ConcreteColleague2();
...
}
|
public class Colleague {
private Mediator mediator;
public Mediator getMediator() { return mediator; }
public void setMediator( Mediator mediator )
{
this.mediator = mediator;
}
}
public class ConcreteColleague1 { }
public class ConcreteColleague2 { }
|
6.Memento (備忘錄模式)
基本概念:
memento是一個保存另外一個對象內部狀態拷貝的對象.這樣以後就可以將該對象恢復到原先保存的狀態.
簡單分析:
Jsp+Javabean中的應用,在Jsp應用中,我們通常有很多表單要求用戶輸入,比如用戶註冊,需要輸入姓名和Email等, 如果一些表項用戶沒有填寫或者填寫錯誤,我們希望在用戶按"提交Submit"後,通過Jsp程序檢查,發現確實有未填寫項目,則在該項目下紅字顯示警告或錯誤,同時,還要顯示用戶剛纔已經輸入的表項.
具體實現:
public class Originator
{
private int number;
private File file = null;
public Originator(){}
// 創建一個Memento
public Memento getMemento(){ return new Memento(this); }
// 恢復到原始值
public void setMemento(Memento m){
number = m.number;
file = m.file;
}
}
private class Memento implements java.io.Serializable{
private int number;
private File file = null;
public Memento( Originator o){
number = o.number;
file = o.file;
}
}
|
7.Observer (觀察者模式)
基本概念:
對象發生變化,會立即通知訂閱者。
簡單分析:
Observer(觀察者)模式是比較常用的一個模式,尤其在界面設計中應用廣泛,而本站所關注的是Java在電子商務系統中應用,因此想從電子商務實例中分析Observer的應用.
雖然網上商店形式多樣,每個站點有自己的特色,但也有其一般的共性,單就"商品的變化,以便及時通知訂戶"這一點,是很多網上商店共有的模式,這一模式類似Observer patern觀察者模式.
具體的說,如果網上商店中商品在名稱價格等方面有變化,如果系統能自動通知會員,將是網上商店區別傳統商店的一大特色.這就需要在商品product中加入Observer這樣角色,以便product細節發生變化時,Observer能自動觀察到這種變化,並能進行及時的update或notify動作.
具體實現:
Java的API還爲爲我們提供現成的Observer接口Java.util.Observer. java.util.Observable
我們只要直接使用它就可以.
public class product extends Observable{
private String name;
private float price;
public String getName(){ return name;}
public void setName(String name)
{ this.name=name; //設置變化點
setChanged();
notifyObservers(name);
}
public float getPrice(){ return price;}
public void setPrice(float price){
this.price=price; //設置變化點
setChanged();
notifyObservers(new Float(price));
} //以下可以是數據庫更新插入命令.
public void saveToDb(){//…..}
}
|
public class NameObserver implements Observer{
private String name=null;
public void update(Observable obj,Object arg){
if (arg instanceof String){
name=(String)arg; //產品名稱改變值在name中
System.out.println("NameObserver :name changet to "+name);
}
}
}
public class PriceObserver implements Observer{
private float price=0;
public void update(Observable obj,Object arg){
if (arg instanceof Float){
price=((Float)arg).floatValue();
System.out.println("PriceObserver :price changet to "+price);
}
}
}
|
Jsp中我們可以來正式執行這段觀察者程序
<jsp:useBean id="product" scope="session" class="Product" /> <jsp:setProperty name="product" property="*" />
<jsp:useBean id="nameobs" scope="session" class="NameObserver" /> <jsp:setProperty name="product" property="*" />
<jsp:useBean id="priceobs" scope="session" class="PriceObserver" /> <jsp:setProperty name="product" property="*" />
<% if (request.getParameter("save")!=null)
{
product.saveToDb();
out.println("產品數據變動保存! 並已經自動通知客戶");
}
else{
//加入觀察者
product.addObserver(nameobs);
product.addObserver(priceobs);
%>
//request.getRequestURI()是產生本jsp的程序名,就是自己調用自己 <form action="<%=request.getRequestURI()%>" method=post>
<input type=hidden name="save" value="1"> 產品名稱:<input type=text name="name" > 產品價格:<input type=text name="price"> <input type=submit>
</form>
<%
}
%>
|
觀察者的Java代碼
public class Test {
public static void main(String args[]){
Product product=new Product();
NameObserver nameobs=new NameObserver();
PriceObserver priceobs=new PriceObserver();
//加入觀察者
product.addObserver(nameobs);
product.addObserver(priceobs);
product.setName("橘子紅了");
product.setPrice(9.22f);
}
}
|
8.State (狀態模式)
基本概念:
不同的狀態,不同的行爲;或者說,每個狀態有着相應的行爲.
簡單分析:
State模式在實際使用中比較多,適合"狀態的切換".因爲我們經常會使用If elseif else 進行狀態切換, 如果針對狀態的這樣判斷切換反覆出現,我們就要聯想到是否可以採取State模式了.
不只是根據狀態,也有根據屬性.如果某個對象的屬性不同,對象的行爲就不一樣,這點在數據庫系統中出現頻率比較高,我們經常會在一個數據表的尾部,加上property屬性含義的字段,用以標識記錄中一些特殊性質的記錄,這種屬性的改變(切換)又是隨時可能發生的,就有可能要使用State.
應用場景:
狀態模式優點:
(1)封裝轉換過程,也就是轉換規則
(2)枚舉可能的狀態,因此,需要事先確定狀態種類。
1.銀行帳戶, 經常會在Open 狀態和Close狀態間轉換.
1.經典的TcpConnection, Tcp的狀態有創建偵聽關閉三個,並且反覆轉換, 其創建偵聽 關閉的具體行爲不是簡單一兩句就能完成的,適合使用State
3.信箱POP帳號, 會有四種狀態, start HaveUsername Authorized quit, 每個狀態對應的行爲應該是比較大的.適合使用State
4. 政府OA中,一個批文的狀態有多種:未辦;正在辦理;正在批示;正在審覈;已經完成等各種狀態,使用狀態機可以封裝這個狀態的變化規則,從而達到擴充狀態時,不必涉及到狀態的使用者。
具體實現:
State需要兩種類型實體參與:
1.state manager 狀態管理器, 就是開關, 如上面例子的Context實際就是一個state manager, 在state manager中有對狀態的切換動作.
2. 用抽象類或接口實現的父類, 不同狀態就是繼承這個父類的不同子類.
狀態抽象
public abstract class State
{
public abstract void handlepush(Context c);
public abstract void handlepull(Context c);
public abstract void getcolor();
}
|
狀態具體實現
public class BlueState extends State
{
public void handlepush(Context c){
//根據push方法"如果是blue狀態的切換到green" ;
c.setState(new GreenState());
}
public void handlepull(Context c){
//根據pull方法"如果是blue狀態的切換到red" ;
c.setState(new RedState());
}
public abstract void getcolor(){ return (Color.blue)}
}
|
State manager
public class Context{
private Sate state=null;
//我們將原來的 Color state 改成了新建的State state;
//setState是用來改變state的狀態使用setState實現狀態的切換
pulic void setState(State state){ this.state=state;}
public void push(){
//狀態的切換的細節部分,在本例中是顏色的變化,已經封裝在子類的handlepush中實現,這裏無需關心
state.handlepush(this);
//因爲sample要使用state中的一個切換結果,使用getColor() Sample sample=new
Sample(state.getColor());
sample.operate();
}
public void pull(){
state.handlepull(this);
Sample2 sample2=new Sample2(state.getColor());
sample2.operate();
}
}
|
9.Strategy (策略模式)
基本概念:
Strategy策略模式是屬於設計模式中對象行爲型模式,主要是定義一系列的算法,把這些算法一個個封裝成單獨的類.
簡單分析:
Stratrgy應用比較廣泛,比如, 公司經營業務變化圖, 可能有兩種實現方式,一個是線條曲線,一個是框圖(bar),這是兩種算法,可以使用Strategy實現.
這裏以字符串替代爲例, 有一個文件,我們需要讀取後,希望替代其中相應的變量,然後輸出.關於替代其中變量的方法可能有多種方法,這取決於用戶的要求,所以我們要準備幾套變量字符替代方案.
應用場景:
Strategy適合下列場合:
1.以不同的格式保存文件;
2.以不同的算法壓縮文件;
3.以不同的算法截獲圖象;
4.以不同的格式輸出同樣數據的圖形,比如曲線或框圖bar等
具體實現:
決策基類
public abstract class RepTempRule{
protected String oldString="";
public void setOldString(String oldString)
{ this.oldString=oldString; }
protected String newString="";
public String getNewString(){ return newString; }
public abstract void replace() throws Exception;
}
|
決策具體類:
public class RepTempRuleOne extends RepTempRule{
public void replace() throws Exception{
//replaceFirst是jdk1.4新特性
newString=oldString.replaceFirst("aaa", "bbbb")
System.out.println("this is replace one");
}
}
public class RepTempRuleTwo extends RepTempRule{
public void replace() throws Exception{
//replaceFirst是jdk1.4新特性
newString=oldString.replaceFirst("aaa", "cccc")
System.out.println("this is replace two");
}
}
|
算法解決類
public class RepTempRuleSolve
{
private RepTempRule strategy;
public RepTempRuleSolve(RepTempRule rule)
{ this.strategy=rule; }
public String getNewContext(Site site,String oldString)
{ return strategy.replace(site,oldString); }
public void changeAlgorithm(RepTempRule newAlgorithm)
{ strategy = newAlgorithm; }
}
|
測試代碼:
public class test{
public void testReplace()
{
//使用第一套替代方案
RepTempRuleSolve solver=new RepTempRuleSolve(new RepTempRuleSimple()); solver.getNewContext(site,context);
//使用第二套
solver=new RepTempRuleSolve(new RepTempRuleTwo());
solver.getNewContext(site,context);
}
|
10.Template (模板模式)
基本概念:
定義一個操作中算法的骨架,將一些步驟的執行延遲到其子類中.
簡單分析:
使用Java的抽象類時,就經常會使用到Template模式,因此Template模式使用很普遍.而且很容易理解和使用。
具體實現:
public abstract class Benchmark {
/** * 下面操作是我們希望在子類中完成 */
public abstract void benchmark();
/** * 重複執行benchmark次數 */
public final long repeat (int count) {
if (count <= 0) return 0;
else {
long startTime = System.currentTimeMillis();
for (int i = 0; i < count; i++)
benchmark();
long stopTime = System.currentTimeMillis();
return stopTime - startTime; }
}
}
|
public class MethodBenchmark extends Benchmark {
/** * 真正定義benchmark內容 */
public void benchmark() {
for (int i = 0; i < Integer.MAX_VALUE;i++){
System.out.printtln("i="+i); } } }
|
11.Visitor (訪問者模式)
基本概念:
作用於某個對象羣中各個對象的操作. 它可以使你在不改變這些對象本身的情況下,定義作用於這些對象的新操作.
在Java中,Visitor模式實際上是分離了collection結構中的元素和對這些元素進行操作的行爲.
簡單分析:
Java的Collection(包括Vector和Hashtable)是我們最經常使用的技術,可是Collection好象是個黑色大染缸,本來有各種鮮明類型特徵的對象一旦放入後,再取出時,這些類型就消失了.那麼我們勢必要用If來判斷
Iterator iterator = collection.iterator()
while (iterator.hasNext()) {
Object o = iterator.next();
if (o instanceof Collection)
messyPrintCollection((Collection)o);
else if (o instanceof String)
System.out.println("'"+o.toString()+"'");
else if (o instanceof Float)
System.out.println(o.toString()+"f");
else
System.out.println(o.toString());
}
|
這樣做的缺點代碼If else if 很繁瑣.我們就可以使用Visitor模式解決它.
使用Visitor模式的前提使用訪問者模式是對象羣結構中(Collection) 中的對象類型很少改變。
在兩個接口Visitor和Visitable中,確保Visitable很少變化,也就是說,確保不能老有新的Element元素類型加進來,可以變化的是訪問者行爲或操作,也就是Visitor的不同子類可以有多種,這樣使用訪問者模式最方便.
如果對象集合中的對象集合經常有變化, 那麼不但Visitor實現要變化,Visistable也要增加相應行爲,GOF建議是,不如在這些對象類中直接逐個定義操作,無需使用訪問者設計模式。
具體實現:
Element定義一個可以接受訪問的接口
public interface Visitable
{
public void accept(Visitor visitor);
}
|
接口具體實現
public class StringElement implements Visitable {
private String value;
public StringElement(String string) { value = string; }
public String getValue(){ return value; }
//定義accept的具體內容這裏是很簡單的一句調用
public void accept(Visitor visitor)
{
visitor.visitString(this);
}
}
public class FloatElement implements Visitable {
private Float value;
public FloatElement(Float value) { this.value = value; }
public Float getValue(){ return value; }
//定義accept的具體內容這裏是很簡單的一句調用
public void accept(Visitor visitor)
{
visitor.visitFloat(this);
}
}
|
訪問者接口
public interface Visitor
{
public void visitString(StringElement stringE);
public void visitFloat(FloatElement floatE);
public void visitCollection(Collection collection);
}
|
public class ConcreteVisitor implements Visitor {
//在本方法中,我們實現了對Collection的元素的成功訪問
public void visitCollection(Collection collection)
{
Iterator iterator = collection.iterator()
while (iterator.hasNext())
{
Object o = iterator.next();
if (o instanceof Visitable) ((Visitable)o).accept(this);
}
}
public void visitString(StringElement stringE) {
System.out.println("'"+stringE.getValue()+"'");
stringE. accept(this);
}
public void visitFloat(FloatElement floatE){
System.out.println(floatE.getValue().toString()+"f");
floatE .accept(this);
}
}
|
測試
Visitor visitor = new ConcreteVisitor();
StringElement stringE = new StringElement("I am a String");
visitor.visitString(stringE);
Collection list = new ArrayList();
list.add(new StringElement("I am a String1"));
list.add(new StringElement("I am a String2"));
list.add(new FloatElement(new Float(12)));
list.add(new StringElement("I am a String3"));
visitor.visitCollection(list);
|