1.單例模式(一個類只有一個對象)
public class Singleton{
private Singleton instance= null;
private Singleton(){
system.out.println("Singleton")
}
public Singleton getInstance(){
if(instance == null){
instance = new Singleton();
}
}
}
2.工廠模式
舉兩個例子以快速明白Java中的簡單工廠模式:
女媧摶土造人
話說:“天地開闢,未有人民,女媧摶土爲人。”女媧需要用土造出一個個的人,但在女媧造出人之前,人的概念只存在於女媧的思想裏面。
女媧造人,這就是簡單工廠模式的應用。
首先,在這個造人的思想裏面,有幾個重要的角色:女媧本身、抽象的人的概念和女媧所造出的一個個具體的人。
1.)女媧是一個工廠類,也就是簡單工廠模式的核心角色。
2.)具休的一個個的人,包括張三,李四等。這些人便是簡單工廠模式裏面的具體產品角色
3.)抽象的人是最早只存在於女媧的頭腦裏的一個想法,女媧按照這個想法造出的一個個具體的人,便都符合這個抽象的人的定義。換言之,這個抽象的想法規定了所有具體的人必須都有的接口(特徵或者功能)
其UML類圖出下所示:
理解了上面的這些東西,再來理解下面的例子,對照理解,相信看完這篇文章,便對java簡單工廠模式有一個很好的理解:
有一個農場公司,專門向市場銷售各類水果,在這個系統裏需要描述下列水果:
葡萄 Grape
草莓 Stuawberry
蘋果 Apple
水果與其他植物不同,最終可以採摘食用,那麼一個自然的做法是建立一個各種水果都適用的接口,以便與其他農場裏的植物區分開來,
此時,則是爲水果類聲明瞭一個接口,表現在代碼上:
2 // 生長
3 void grow();
4 // 收穫
5 void harvest();
6 // 種植
7 void plant();
8 }
9
10
水果接口規定出所有的水果必須實現的接口,包括任何水果類必須具備的方法plant(),grow(),和harvest();
Apple類是水果類的一種,因此它實現了水果接口所聲明的所有方法。另處,由於蘋果是多年生植物,因此多出一個treeAge性質,描述蘋果的樹齡。代碼如下所示:
public class Apple implements Fruit { // 通過implements實現接口Fruit
private int treeAge;
public void grow() {
log( " Apple is growing " );
}
public void harvest() {
log( " Apple has been harvested " );
}
public void plant() {
log( " Apple ha been planted " );
}
public static void log(String msg) {
System.out.println(msg);
}
public int getTreeAge() {
return treeAge;
}
public void setTreeAge( int treeAge) {
this .treeAge = treeAge;
}
}
同理,葡萄 Grape:
public class Grape implements Fruit{
private boolean seedless;
public void grow(){
log("Grape is growing.");
}
public void harvest(){
log("Grape has been harvested");
}
public void plant(){
log("Grape ha been planted");
}
public static void log(String msg){
System.out.println(msg);
}
public boolean isSeedless() {
return seedless;
}
public void setSeedless(boolean seedless) {
this.seedless = seedless;
}
}
草莓 Stuawberry:
public class Strawberry implements Fruit{
public void grow(){
log("Strawberry is growing");
}
public void harvest(){
log("Strawberry has been harvested");
}
public void plant(){
log("Strawberry has been planted");
}
public static void log(String msg){
System.out.println(msg);
}
}
農場園丁也是系統的一部分,由一個類來代表,FruitGardener類,代碼如下:
public class FruitGardener{
public static Fruit factory(String which)throws Exception{
if(which.equalsIgnoreCase("apple")){
return new Apple();
}else if(which.equalsIgnoreCase("strawberry")){
return new Strawberry();
}else if (which.equalsIgnoreCase("grape")){
return new Grape();
}else{
throw new Exception("Bad fruit request");
}
}
}
public class People {
public static void main(String[] args) throws Exception {
FruitGardener fg=new FruitGardener();
Fruit ap=fg.factory("Apple");
ap.grow();
Fruit gp=fg.factory("Grape");
gp.plant();
Fruit dd=fg.factory("ddd");//拋出Bad fruit request異常
}
}
(注:以上代碼在JDK5.0,Myeclise3.2下編譯通過)
類比兩個例子,園丁就相當於女媧,而水果就相當於具體的人,接口水果類就相當於存在於類女媧思想裏的人的抽象概念。
由以上兩個例子可得出,簡單工廠模式需要由以下角色組成:
接口
接口的實現類(簡單工廠模式裏面的具體產品角色)
工廠
理解了以下兩個例子,再來看第三個例子:
注意對比以下三個實例的不同
實例1:
//定義接口
interface Car{
public void run();
public void stop();
}
//具體實現類
class Benz implements Car{
public void run(){
System.out.println("Benz開始啓動了。。。。。");
}
public void stop(){
System.out.println("Benz停車了。。。。。");
}
}
//具體實現類
class Ford implements Car{
public void run(){
System.out.println("Ford開始啓動了。。。");
}
public void stop(){
System.out.println("Ford停車了。。。。");
}
}
//工廠
class Factory{
public static Car getCarInstance(){
return new Ford();
}
}
public class FactoryDemo01 {
public static void main(String[] args) {
Car c=Factory.getCarInstance();
c.run();
c.stop();
}
}
實例二:
//定義接口
interface Car{
public void run();
public void stop();
}
//具體實現類
class Benz implements Car{
public void run(){
System.out.println("Benz開始啓動了。。。。。");
}
public void stop(){
System.out.println("Benz停車了。。。。。");
}
}
class Ford implements Car{
public void run(){
System.out.println("Ford開始啓動了。。。");
}
public void stop(){
System.out.println("Ford停車了。。。。");
}
}
//工廠
class Factory{
public static Car getCarInstance(String type){
Car c=null;
if("Benz".equals(type)){
c=new Benz();
}
if("Ford".equals(type)){
c=new Ford();
}
return c;
}
}
public class FactoryDemo02 {
public static void main(String[] args) {
Car c=Factory.getCarInstance("Benz");
if(c!=null){
c.run();
c.stop();
}else{
System.out.println("造不了這種汽車。。。");
}
}
}
實例三:
public void run();
public void stop();
}
class Benz implements Car{
public void run(){
System.out.println("Benz開始啓動了。。。。。");
}
public void stop(){
System.out.println("Benz停車了。。。。。");
}
}
class Ford implements Car{
public void run(){
System.out.println("Ford開始啓動了。。。");
}
public void stop(){
System.out.println("Ford停車了。。。。");
}
}
class Toyota implements Car{
public void run(){
System.out.println("Toyota開始啓動了。。。");
}
public void stop(){
System.out.println("Toyota停車了。。。。");
}
}
class Factory{
public static Car getCarInstance(String type){
Car c=null;
try {
c=(Car)Class.forName("org.jzkangta.factorydemo03."+type).newInstance();//利用反射得到汽車類型
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return c;
}
}
public class FactoryDemo03 {
public static void main(String[] args) {
Car c=Factory.getCarInstance("Toyota");
if(c!=null){
c.run();
c.stop();
}else{
System.out.println("造不了這種汽車。。。");
}
}
}
對比三個實例:
實例一,雖然實現了簡單工廠,但每次只能得到一種汽車,如果我們想換一種,就得修改工廠,太不方便,而實例二則改變了這種情況,便得我們可以按照我們的需要更換汽車,但我們所更換的汽車必須是實現類中有的,如果我們想要增加一種汽車的時候,我們還是得更改工廠,通過改進,實例三利用反射機制,得到汽車類型,這樣當我們需要增加一種新的汽車時,就無需要再修改工廠,而只需要增加要實現的類即可。也就是說要增加什麼樣的汽車直接增加這個汽車的類即可,而無需改變工廠。從而達到了工廠分離的效果。
3.代理模式(中介)
代理模式的作用是:爲其他對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個客戶不想或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。
代理模式一般涉及到的角色有:
抽象角色:聲明真實對象和代理對象的共同接口;
代理角色:代理對象角色內部含有對真實對象的引用,從而可以操作真實對象,同時代理對象提供與真實對象相同的接口以便在任何時刻都能代替真實對象。同時,代理對象可以在執行真實對象操作時,附加其他的操作,相當於對真實對象進行封裝。
真實角色:代理角色所代表的真實對象,是我們最終要引用的對象。
以下以《Java與模式》中的示例爲例:
代碼:
abstract public class Subject{
abstract public void request();
}
//真實角色:實現了Subject的request()方法。
public class RealSubject extends Subject{
public RealSubject(){
}
public void request(){
System.out.println("From real subject.");
}
}
//代理角色:
public class ProxySubject extends Subject{
private RealSubject realSubject; //以真實角色作爲代理角色的屬性
public ProxySubject(){
}
public void request(){ //該方法封裝了真實對象的request方法
preRequest();
if( realSubject == null ){
realSubject = new RealSubject();
}
realSubject.request(); //此處執行真實對象的request方法
postRequest();
}
private void preRequest(){
//something you want to do before requesting
}
private void postRequest(){
//something you want to do after requesting
}
}
//客戶端調用:
Subject sub=new ProxySubject();
Sub.request();
4.命令模式(中介)
(1)命令模式定義
將一個請求封裝爲一個對象,從而使你可用不同的請求對客戶進行參數化;對請求排隊或記錄請求日誌,以及支持可撤銷的操作。
(2)應用命令模式來解決的思路
首先來看看實際電腦的解決方案
先畫個圖來描述一下,看看實際的電腦是如何處理上面描述的這個問題的,如圖1所示:
圖1 電腦操作示意圖
當客戶按下按鈕的時候,按鈕本身並不知道如何處理,於是通過連接線來請求主板,讓主板去完成真正啓動機器的功能。
這裏爲了描述它們之間的關係,把主板畫到了機箱的外面。如果連接線連接到不同的主板,那麼真正執行按鈕請求的主板也就不同了,而客戶是不知道這些變化的。
通過引入按鈕和連接線,來讓發出命令的客戶和命令的真正實現者——主板完全解耦,客戶操作的始終是按鈕,按鈕後面的事情客戶就統統不管了。
要用程序來解決上面提出的問題,一種自然的方案就是來模擬上述解決思路。
在命令模式中,會定義一個命令的接口,用來約束所有的命令對象,然後提供具體的命令實現,每個命令實現對象是對客戶端某個請求的封裝,對應於機箱上的按鈕,一個機箱上可以有很多按鈕,也就相當於會有多個具體的命令實現對象。
在命令模式中,命令對象並不知道如何處理命令,會有相應的接收者對象來真正執行命令。就像電腦的例子,機箱上的按鈕並不知道如何處理功能,而是把這個請求轉發給主板,由主板來執行真正的功能,這個主板就相當於命令模式的接收者。
在命令模式中,命令對象和接收者對象的關係,並不是與生俱來的,需要有一個裝配的過程,命令模式中的Client對象就來實現這樣的功能。這就相當於在電腦的例子中,有了機箱上的按鈕,也有了主板,還需要有一個連接線把這個按鈕連接到主板上才行。
命令模式還會提供一個Invoker對象來持有命令對象,就像電腦的例子,機箱上會有多個按鈕,這個機箱就相當於命令模式的Invoker對象。這樣一來,命令模式的客戶端就可以通過Invoker來觸發並要求執行相應的命令了,這也相當於真正的客戶是按下機箱上的按鈕來操作電腦一樣。
模式結構和說明
命令模式的結構如圖2所示:
圖2 命令模式結構圖
Command:
定義命令的接口,聲明執行的方法。
ConcreteCommand:
命令接口實現對象,是“虛”的實現;通常會持有接收者,並調用接收者的功能來完成命令要執行的操作。
Receiver:
接收者,真正執行命令的對象。任何類都可能成爲一個接收者,只要它能夠實現命令要求實現的相應功能。
Invoker:
要求命令對象執行請求,通常會持有命令對象,可以持有很多的命令對象。這個是客戶端真正觸發命令並要求命令執行相應操作的地方,也就是說相當於使用命令對象的入口。
Client:
創建具體的命令對象,並且設置命令對象的接收者。注意這個不是我們常規意義上的客戶端,而是在組裝命令對象和接收者,或許,把這個Client稱爲裝配者會更好理解,因爲真正使用命令的客戶端是從Invoker來觸發執行。
命令模式示例代碼
(1)先來看看命令接口的定義,示例代碼如下:
/** * 命令接口,聲明執行的操作 */ public interface Command { /** * 執行命令對應的操作 */ public void execute(); } |
(2)再來看看具體的命令實現對象,示例代碼如下:
/**
* 具體的命令實現對象
*/
public class ConcreteCommand implements Command {
/**
* 持有相應的接收者對象
*/
private Receiver receiver = null;
/**
* 示意,命令對象可以有自己的狀態
*/
private String state;
/**
* 構造方法,傳入相應的接收者對象
* @param receiver 相應的接收者對象
*/
public ConcreteCommand(Receiver receiver){
this.receiver = receiver;
}
public void execute() {
//通常會轉調接收者對象的相應方法,讓接收者來真正執行功能
receiver.action();
}
}
</pre><pre>
(3)再來看看接收者對象的實現示意,示例代碼如下:
/** * 接收者對象 */ public class Receiver { /** * 示意方法,真正執行命令相應的操作 */ public void action(){ //真正執行命令操作的功能代碼 } } |
(4)接下來看看Invoker對象,示例代碼如下:
/** * 調用者 */ public class Invoker { /** * 持有命令對象 */ private Command command = null; /** * 設置調用者持有的命令對象 * @param command 命令對象 */ public void setCommand(Command command) { this.command = command; } /** * 示意方法,要求命令執行請求 */ public void runCommand() { //調用命令對象的執行方法 command.execute(); } } |
(5)再來看看Client的實現,注意這個不是我們通常意義上的測試客戶端,主要功能是要創建命令對象並設定它的接收者,因此這裏並沒有調用執行的代碼,示例代碼如下:
public class Client { /** * 示意,負責創建命令對象,並設定它的接收者 */ public void assemble(){ //創建接收者 Receiver receiver = new Receiver(); //創建命令對象,設定它的接收者 Command command = new ConcreteCommand(receiver); //創建Invoker,把命令對象設置進去 Invoker invoker = new Invoker(); invoker.setCommand(command); } } |
5.策略模式
策略模式中有三個對象:(1) 環境對象:該類中實現了對抽象策略中定義的接口或者抽象類的引用。(2) 抽象策略對象:它可由接口或抽象類來實現。(3) 具體策略對象:它封裝了實現同不功能的不同算法。利用策略模式構建應用程序,可以根據用戶配置等內容,選擇不同有算法來實現應用程序的功能。具體的選擇有環境對象來完成。採用這種方式可以避免由於使用條件語句而帶來的代碼混亂,提高應用程序的靈活性與條理性。
應用場景舉例:
劉備要到江東娶老婆了,走之前諸葛亮給趙雲(伴郎)三個錦囊妙計,說是按天機拆開能解決棘手問題,嘿,還別說,真解決了大問題,搞到最後是周瑜陪了夫人又折兵,那咱們先看看這個場景是什麼樣子的。
先說說這個場景中的要素:三個妙計,一個錦囊,一個趙雲,妙計是亮哥給的,妙計放在錦囊裏,俗稱就是錦囊妙計嘛,那趙雲就是一個幹活的人,從錦囊取出妙計,執行,然後獲勝。用java程序怎麼表現這些呢?
那我們先來看看圖?
三個妙計是同一類型的東西,那咱就寫個接口:
- package com.yangguangfu.strategy;
- /**
- *
- * @author [email protected]:阿福
- * 首先定義一個策略接口,這是諸葛亮老人家給趙雲的三個錦囊妙計的接口。
- */
- public interface IStrategy {
- //每個錦囊妙計都是一個可執行的算法。
- public void operate();
- }
然後再寫三個實現類,有三個妙計嘛:
妙計一:初到吳國:
- package com.yangguangfu.strategy;
- /**
- *
- * @author [email protected]:阿福
- * 找喬國老幫忙,使孫權不能殺劉備。
- */
- public class BackDoor implements IStrategy {
- @Override
- public void operate() {
- System.out.println("找喬國老幫忙,讓吳國太給孫權施加壓力,使孫權不能殺劉備...");
- }
- }
妙計二:求吳國太開個綠燈,放行:
- package com.yangguangfu.strategy;
- /**
- *
- * @author [email protected]:阿福
- * 求吳國太開個綠燈。
- */
- public class GivenGreenLight implements IStrategy {
- @Override
- public void operate() {
- System.out.println("求吳國太開個綠燈,放行!");
- }
- }
妙計三:孫夫人斷後,擋住追兵:
- package com.yangguangfu.strategy;
- /**
- *
- * @author [email protected]:阿福
- * 孫夫人斷後,擋住追兵。
- */
- public class BlackEnemy implements IStrategy {
- @Override
- public void operate() {
- System.out.println("孫夫人斷後,擋住追兵...");
- }
- }
好了,大家看看,三個妙計是有了,那需要有個地方放妙計啊,放錦囊裏:
- package com.yangguangfu.strategy;
- /**
- *
- * @author [email protected]:阿福
- *
- */
- public class Context {
- private IStrategy strategy;
- //構造函數,要你使用哪個妙計
- public Context(IStrategy strategy){
- this.strategy = strategy;
- }
- public void operate(){
- this.strategy.operate();
- }
- }
然後就是趙雲雄赳赳的揣着三個錦囊,拉着已步入老年行列,還想着娶純情少女的,色咪咪的劉備老爺子去入贅了,嗨,還別說,亮哥的三個妙計還真不錯,瞧瞧:
- package com.yangguangfu.strategy;
- public class ZhaoYun {
- /**
- * 趙雲出場了,他根據諸葛亮給他的交代,依次拆開妙計
- */
- public static void main(String[] args) {
- Context context;
- //剛到吳國的時候拆開第一個
- System.out.println("----------剛剛到吳國的時候拆開第一個---------------");
- context = new Context(new BackDoor());
- context.operate();//拆開執行
- System.out.println("\n\n\n\n\n\n\n\n\n\n\n\n\n");
- //當劉備樂不思蜀時,拆開第二個
- System.out.println("----------劉備樂不思蜀,拆第二個了---------------");
- context = new Context(new GivenGreenLight());
- context.operate();//拆開執行
- System.out.println("\n\n\n\n\n\n\n\n\n\n\n\n\n");
- //孫權的小追兵了,咋辦?拆開第三個錦囊
- System.out.println("----------孫權的小追兵了,咋辦?拆開第三個錦囊---------------");
- context = new Context(new BlackEnemy());
- context.operate();//拆開執行
- System.out.println("\n\n\n\n\n\n\n\n\n\n\n\n\n");
- }
- }
後話:就這三招,搞得的周郎是“賠了夫人又折兵”呀!這就是策略模式,高內聚低耦合的特點也表現出來了,還有一個就是擴展性,也就是OCP原則,策略類可以繼續添加下去氣,只是修改Context.java就可以了,這個不多說了,自己領會吧。
注: 策略模式與工廠模式比較說你要去買件衣服,給你50塊錢,策略模式的做法就是去京東、噹噹、淘寶、卓越等網上去看,然後決定要買那一件。而工廠模式的做法確實,告訴系統我需要用50塊錢買件衣服,到底他去噹噹、淘寶、京東、卓越你不關心,你只需要50塊錢的一件衣服。淘寶mm一語道出工廠相當於黑盒子,策略相當於白盒子。
6.門面模式
子系統角色中的類:
- public class ModuleA {
- //示意方法
- public void testA(){
- System.out.println("調用ModuleA中的testA方法");
- }
- }
- public class ModuleB {
- //示意方法
- public void testB(){
- System.out.println("調用ModuleB中的testB方法");
- }
- }
- public class ModuleC {
- //示意方法
- public void testC(){
- System.out.println("調用ModuleC中的testC方法");
- }
- }
門面角色類:
- public class Facade {
- //示意方法,滿足客戶端需要的功能
- public void test(){
- ModuleA a = new ModuleA();
- a.testA();
- ModuleB b = new ModuleB();
- b.testB();
- ModuleC c = new ModuleC();
- c.testC();
- }
- }
客戶端角色類:
- public class Client {
- public static void main(String[] args) {
- Facade facade = new Facade();
- facade.test();
- }
- }
Facade類其實相當於A、B、C模塊的外觀界面,有了這個Facade類,那麼客戶端就不需要親自調用子系統中的A、B、C模塊了,也不需要知道系統內部的實現細節,甚至都不需要知道A、B、C模塊的存在,客戶端只需要跟Facade類交互就好了,從而更好地實現了客戶端和子系統中A、B、C模塊的解耦,讓客戶端更容易地使用系統。
7.橋接模式
先看一下類結構圖:
代碼實現:
- abstract class AbstractRoad{
- AbstractCar aCar;
- void run(){};
- }
- abstract class AbstractCar{
- void run(){};
- }
- class Street extends AbstractRoad{
- @Override
- void run() {
- // TODO Auto-generated method stub
- super.run();
- aCar.run();
- System.out.println("在市區街道行駛");
- }
- }
- class SpeedWay extends AbstractRoad{
- @Override
- void run() {
- // TODO Auto-generated method stub
- super.run();
- aCar.run();
- System.out.println("在高速公路行駛");
- }
- }
- class Car extends AbstractCar{
- @Override
- void run() {
- // TODO Auto-generated method stub
- super.run();
- System.out.print("小汽車");
- }
- }
- class Bus extends AbstractCar{
- @Override
- void run() {
- // TODO Auto-generated method stub
- super.run();
- System.out.print("公交車");
- }
- }
- public static void main(String[] args){
- AbstractRoad speedWay = new SpeedWay();
- speedWay.aCar = new Car();
- speedWay.run();
- AbstractRoad street = new Street();
- street.aCar = new Bus();
- street.run();
- }
可以看到,通過對象組合的方式,Bridge 模式把兩個角色之間的繼承關係改爲了耦合的關係,從而使這兩者可以從容自若的各自獨立的變化,這也是Bridge模式的本意。
8.觀察者模式
觀察者模式定義了一種一對多的依賴關係,讓多個觀察者對象同時監聽某一個主題對象。
這個主題對象在狀態上發生變化時,會通知所有觀察者對象,讓它們能夠自動更新自己。
觀察者模式的組成
抽象主題角色:把所有對觀察者對象的引用保存在一個集合中,每個抽象主題角色都可以有任意數量的觀察者。抽象主題提供一個接口,可以增加和刪除觀察者角色。一般用一個抽象類和接口來實現。
抽象觀察者角色:爲所有具體的觀察者定義一個接口,在得到主題的通知時更新自己。
具體主題角色:在具體主題內部狀態改變時,給所有登記過的觀察者發出通知。具體主題角色通常用一個子類實現。
具體觀察者角色:該角色實現抽象觀察者角色所要求的更新接口,以便使本身的狀態與主題的狀態相協調。通常用一個子類實現。如果需要,具體觀察者角色可以保存一個指向具體主題角色的引用。
程序實例
通過程序實例來說明觀察者模式:
首先定義抽象的觀察者:
//抽象觀察者角色 public interface Watcher { public void update(String str); }
然後定義抽象的主題角色,即抽象的被觀察者,在其中聲明方法(添加、移除觀察者,通知觀察者):
//抽象主題角色,watched:被觀察 public interface Watched { public void addWatcher(Watcher watcher); public void removeWatcher(Watcher watcher); public void notifyWatchers(String str); }
然後定義具體的觀察者:
public class ConcreteWatcher implements Watcher { @Override public void update(String str) { System.out.println(str); } }
之後是具體的主題角色:
import java.util.ArrayList; import java.util.List; public class ConcreteWatched implements Watched { // 存放觀察者 private List<Watcher> list = new ArrayList<Watcher>(); @Override public void addWatcher(Watcher watcher) { list.add(watcher); } @Override public void removeWatcher(Watcher watcher) { list.remove(watcher); } @Override public void notifyWatchers(String str) { // 自動調用實際上是主題進行調用的 for (Watcher watcher : list) { watcher.update(str); } } }
編寫測試類:
public class Test { public static void main(String[] args) { Watched girl = new ConcreteWatched(); Watcher watcher1 = new ConcreteWatcher(); Watcher watcher2 = new ConcreteWatcher(); Watcher watcher3 = new ConcreteWatcher(); girl.addWatcher(watcher1); girl.addWatcher(watcher2); girl.addWatcher(watcher3); girl.notifyWatchers("開心"); } }