五大設計模式
最關鍵的軟件開發工具是受過良好設計原則訓練的思維
一、單例模式
Singleton類定義一個個getInstance()操作,允許客戶端訪問他的唯一實例,getInstance()是一個靜態的方法,主要創建自己的一個唯一實例。
比如我們希望整個應用程序只有一個連接數據庫connection實例。
又比如要求一個應用程序中只存在某個用戶數據結構的唯一結構。
某個類只有一個實例,具體的代碼實現:
class ClassicSingleton {
private static ClassicSingleton instance;
private ClassicSingleton(){
//構造方法爲“私有”,阻止了外界用new創建本類實例的可能
}
public static synchronized ClassicSingleton getInstance(){
if(instance==null){
instance = new ClassicSingleton();
//return new ClassicSingleton();
}
return instance;
}
}
/**
*
* @author Yue
* @version 0.1
*
*/
public class TestSingleton {
public static void main(String[] args) {
// TODO Auto-generated method stub
ClassicSingleton s1 = ClassicSingleton.getInstance();
ClassicSingleton s2 = ClassicSingleton.getInstance();
System.out.println(s1==s2);//這個是相等的
}
}
二、工廠模式(簡單工廠、工廠模式、抽象工廠)
所有的產品和產品接口組成一個產品羣,這個就是“簡單工廠”,只有一個工廠。
工廠和產品最好在一個包裏,產品類就可以不是public的,這樣只有工廠纔可以訪問產品,其他不在這個包裏就無法直接訪問這些產品,起到屏蔽產品類的作用。
可以看着下面的代碼來理解一下簡單工廠模式具體代碼說明
//產品接口(如果在接口這個抽象類中需要包含屬性數據,則將接口改爲抽象類Abstract)
public interface ProductInterface {
public void method();
}
//衣服產品
public class Clothes implements ProductInterface {
public void method(){
}
}
//褲子產品
public class Trousers implements ProductInterface {
public void method(){
}
}
//帽子產品
public class Cap implements ProductInterface {
public void method(){
}
}
public class Factory {
public ProductInterface createProduct(String productname){
ProductInterface product = null;
if(productname == "clothes"){
product = new Clothes();
}
if(productname == "trousers"){
product = new Trousers();
}
if(productname == "cap"){
product = new Cap();
}
return product;
}
}
//要生產哪種產品,需要告訴工廠,即給工廠方法傳遞一個參數“productname”,
//工廠方法變爲有參數的方法。
//工廠類不管是生產一種產品還是生產幾種產品,都只有一個工廠,這種工廠叫“簡單工廠”。
public class XYZ { // 客戶端代碼
private ProductInterface product;
private Factory factory;
public void work(){
factory = new Factory();
product = factory.createProduct("clothes");
product.method();
}
}
這樣做的優點:我們可以對創建的對象進行一些 “加工” ,而且客戶端並不知道,因爲工廠隱藏了這些細節。如果,沒有工廠的話,那我們是不是就得自己在客戶端上寫這些代碼,這就好比本來可以在工廠裏生產的東西,拿來自己手工製作,不僅麻煩以後還不好維護。
但是缺點也很明顯:如果需要在方法裏寫很多與對象創建有關的業務代碼,而且需要的創建的對象還不少的話,我們要在這個簡單工廠類裏編寫很多個方法,每個方法裏都得寫很多相應的業務代碼,而每次增加子類或者刪除子類對象的創建都需要打開這簡單工廠類來進行修改。這會導致這個簡單工廠類很龐大臃腫、耦合性高,而且增加、刪除某個子類對象的創建都需要打開簡單工廠類來進行修改代碼也違反了開-閉原則。
式
這時候就需要工廠模式了
工廠模式實現如下
1.首先,先定義一個工廠接口
public interface Factory {
public ProductInterface createProduct() ;
}
2.然後是具體的工廠類
// 衣服類工廠
public class ClothesFactory implements Factory{
public ProductInterface createProduct() {
System.out.println("生產衣服");
return new Clothes();
}
}
// 鞋類工廠
public class ShoeFactory implements Factory{
public ProductInterface createProduct() {
System.out.println("生產鞋子");
return new Shoes();
}
}
........
3.工廠接口與產品的內容與簡單一樣
4.客戶端
public class Client {
public static void main(String[] args) throws Exception {
// 使用反射機制實例化工廠對象,因爲字符串是可以通過變量改變的
Factory ClothesFactory = (Factory) Class.forName("org.zero01.factory.ClothesFactory ").newInstance();
Factory ShoseFactory=(Factory) Class.forName("org.zero01.factory.ShoseFactory").newInstance();
// 通過工廠對象創建相應的實例對象
ProductInterface clothes= ClothesFactory .createProduct();
ProductInterface shoes= ShoseFactory.createProduct();
System.out.println(clothes.methods());
System.out.println(shoes.methods());
}
}
工廠模式中,要增加產品類時也要相應地增加工廠類,客戶端的代碼也增加了不少。工廠方法把簡單工廠的內部邏輯判斷轉移到了客戶端代碼來進行。
你想要加功能,本來是改工廠類的,而現在是修改客戶端。而且各個不同功能的實例對象的創建代碼,也沒有耦合在同一個工廠類裏,這也是工廠方法模式對簡單工廠模式解耦的一個體現。工廠方法模式克服了簡單工廠會違背開-閉原則的缺點,又保持了封裝對象創建過程的優點。
但工廠方法模式的缺點是每增加一個產品類,就需要增加一個對應的工廠類,增加了額外的開發量。
有興趣的可以參考這個文件深入瞭解簡單工廠模式,工廠模式以及抽象工廠模式(具體)
三、適配器模式
適配器模式主要應用於希望複用一些現存的類,但是接口又與複用環境要求不一致的情況。當希望複用的類接口不對時,採用適配器使控制之外的一個原有對象與某個接口匹配。
代碼實現
//客戶端:
public class XYZ {
public void work()
{
Target tag = new RealizeClass();
tag.Request();
}
}
//服務接口:
public interface Target
{
public void Request();
}
// 客戶端想要的服務:
public class Service
{
public void SpecificRequest() //但是接口不匹配
{
//具體操作方法
}
}
//重寫服務端實現類,作爲適配器,代碼爲:
public class RealizeClass implements Target
{
private Service service;
public void Request()
{
service = new Service();
service.SpecificRequest(); //進行適配
}
}
// 例子2
public interface Duck{ //鴨子具有呱呱叫和飛行功能
public void quack();
public void fly();
}
Public class MallardDuck implements Duck{
public void quack(){
System.out.println(“Quack”);
}
public void fly(){
System.out.println(“I am flying”);
}
}
public interface Turkey{ //火雞具有咯咯叫和飛行功能
public void gobble();
public void fly();
}
Public class WildTurkey implements Turkey{
public void gobble(){
System.out.println(“Gobble gobble”);
}
public void fly(){
System.out.println(“I am flying a short distance”);
}
}
/**
* 假設你缺少鴨子對象,想用一些火雞對象來冒充
* 因爲火雞的接口不同,所以我們不能公然拿來用,寫個適配器
**/
public class TurkeyAdapter implements Duck{ //實現想轉化的類型
Turkey turkey;
//利用構造器取得適配器的構造引用
public TurkeyAdapter (Turkey turkey){
this.turkey = turkey;
}
public void quack(){
turkey.gobble();
}
public void fly(){ //火雞飛行距離短,所以需要5次飛行對應鴨子的飛行
for(int i=0;i<5;i++){
turkey.fly();
}
}
}
public class DuckTestDrive{
public static void main(String[] args){
MallardDuck duck = new MallardDuck();
WildTurkey turkey = new WildTurkey();
// 把火雞放進火雞適配器,讓它看起來像個鴨子
Duck turkeyAdapter = new TurkeyAdapter(turkey);
System.out.println(“The turkey says…”);
turkey.gobble();
turkey.fiy();
System.out.println(“\nThe Duck says…”);
testDuck(duck);
System.out.println(“\nThe TurkeyAdapter says…”);
testDuck(turkeyAdapter);
}
static void testDuck(Duck duck){
duck.quack();
duck.fly();
}
}
結果:
The turkey says…
Gobble gobble
I am flying a short distance
The Duck says…
Quack
I am flying
The TurkeyAdapter says…
Gobble gobble
I am flying a short distance
I am flying a short distance
I am flying a short distance
I am flying a short distance
I am flying a short distance
四、模板模式
模板模式 :解決某類事情的步驟有些是固定的,有些是會發生變化的,那麼這時候我們可以爲這類事情提供一個模板代碼,從而提高效率。
通過定義一個算法骨架,而將算法中的步驟延遲到子類,這樣子類就可以複寫這些步驟的實現來實現特定的算法。
是類的一種行爲,只需要準備一個抽象類,將邏輯用具體方法和構造函數的形式來表現,後聲明一些抽象方法來迫使子類必須實現其邏輯,不同的子類可以實現不同的方法,從而可以讓剩餘的邏輯有不同的實現。即可以定義抽象的方法,讓子類實現剩餘的邏輯。
分類:
抽象模版:定義的數量和類型,定義了一個抽象操作讓子類實現,定義並實現了一個模版方法,該模版方法一般是一個具體方法,同時給出了頂層邏輯的框架,具體的步驟在相應的抽象操作中,具體的業務邏輯延遲到子類實現。(只有一個抽象基類)
具體模版:模版方法的數量,實現父類定義的一個或多個抽象方法,每個抽象模版的角色都可以由任意多個具體模版角色與之對應。即不是一對一,而是多對多。
例如:
1.辦理銀行業務:
- 進門取號
- 填寫單據(每個客戶填寫的單據都不一樣,因業務不同而不同)
- 等待叫號
- 窗口辦理
2.使用場景
- 多個子類公有的方法,並且邏輯基本相同時
- 重要、複雜的算法,可以把核心算法設計爲模版方法
- 重構時,模版方法模式是一個經常使用的模式
3.UML結構圖:
詳情可以參考這個文檔
來一個例子:
public class Coffce{
void prepareRecipe(){
boilWater();
brewCoffeeGrinds(); // 沖泡咖啡
pourInCup();
addSugarAndMilk();
}
public void boilWater(){
// …
}
public void brewCoffeeGrinds(){
//…
}
public void pourInCup(){
//…
}
public void addSugarAndMilk(){
//…
}
}
public class Tea{
void prepareRecipe(){
boilWater();
steepTeaBag(); //浸泡茶包
pourInCup();
addLemon();
}
public void boilWater(){
// …
}
public void steepTeaBag(){
//…
}
public void pourInCup(){
//…
}
public void addLemon(){
//…
}
}
有公共函數,該如何設計?
void prepareRecipe(){
boilWater();
brewCoffeeGrinds();
pourInCup();
addSugarAndMilk();
}
void prepareRecipe(){
boilWater();
steepTeaBag();
pourInCup();
addLemon();
}
//生成抽象類CaffeineBeverage
Public abstract class CaffeineBeverage{ //咖啡因飲料
final void prepareRecipe(){
boilWater();
brew();
pourInCup();
addCondiments();
}
public void boilWater(){ // 此處是否還可以改進?
// …
}
abstract void brew();
public void pourInCup(){
//…
}
abstract void addCondiments();
}
//實現抽象類
public class Tea extends CaffeineBeverage{
public void brew(){
// …
}
public void addCondiments(){
//…
}
}
public class Coffee extends CaffeineBeverage{
public void brew(){
// …
}
public void addCondiments(){
//…
}
}
如果我不想加任何添加,怎麼辦?
// 引入鉤子方法
Public abstract class CaffeineBeverage{ //咖啡因飲料
public final void prepareRecipe(){
boilWater();
brew();
pourInCup();
if(isCustomerWantsCondiments()){
addCondiments();
}
}
private void boilWater(){
// …
}
protected abstract void brew();
private void pourInCup(){
//…
}
protected abstract void addCondiments();
// 詢問用戶是否加入調料 Hook方法(鉤子方法)
// 有一個默認輸出,子類決定是否掛鉤(覆蓋其父類的方法)
protected boolean isCustomerWantsCondiments(){
return true;
}
}
// 使用鉤子
Public class CoffeeWithHook extends CaffeineBeverage{ //咖啡因飲料
//…省略子類中覆蓋的其他方法
public boolean isCustomerWantsCondiments(){
String answer = getUserInput();
if(answer.toLowerCase().startsWith(“y”)){
return true;
}else{
return false;
}
}
private String getUserInput(){
String answer = null;
BufferedReader in = new BufferedReader
(new InputStreamReader(System.in));
try{
answer = in.readLine();
}catch(IOException ioe){
// 做io異常處理
}
if(answer == null){return “no”;}
return answer;
}
}
五、(代理、門面)
利用門面模式實現各層之間的關聯與協作。
如:某層中的各個組件需要訪問另一層中的不同組件,導致層與層之間出現“多對多”的關聯形式
解決辦法: 用“門面架構模式”分離“多對多”的關聯。
門面模塊與中介的區別:
門面模塊:門面組件之後的各個組件之間有可能仍有相互之間的關聯。
中介模塊:中介模塊引入後,使與中介關聯的各個組件之間的關聯被斷絕,他們都只與中介打交道,如下面中介模式的圖所示