學習設計模式本來是我在找工作時用來復習的Java基礎。參考了《Android 源碼設計模式解析與實戰》一書和其他博主的博客,再加上自己的理解和總結,就有了這篇博客。
設計模式可以說是和每一個開發者都密切相關,比如像一個安卓開發程序員,使用RecyclerView的setAdapter
和notifidatasetchange
這兩個方法就用到了適配器模式和觀察者模式,同時如果想成為一個架構師,這些設計模式的知識也會讓你搭建框架的時候得心應手。
目前設計模式為23種,這些是目前公認的,我的理解是:如果自己有更好的想法也可以寫出更好的設計模式,就像mvc,mvp這些,都是別人參悟出來的一些架構方式,后來被人公認才廣泛流行起來,當然設計模式也不是都一定是完美的,每個設計模式都是優缺點并存。
本文將介紹部分設計模式:
- 構造者模式
- 工廠模式
- 責任鏈模式
- 觀察者模式
- 原型模式
- 代理模式
- 單例模式
- 狀態模式
- 策略模式
- 適配器模式
其他的設計模式等學習完之后,再去補充一篇博客(二)即可。
1、構造者模式
如果用過Retorfit、OKHttp等對以下代碼一定不會陌生。
public void init() {
// 初始化okhttp
OkHttpClient client = new OkHttpClient.Builder()
.build();
// 初始化Retrofit
retrofit = new Retrofit.Builder()
.client(client)
.baseUrl(Request.HOST)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
}
Retrofit的初始化就是一個典型的構造者模式。
1.1、構造者模式的定義
將一個復雜對象的構建和它的初始化進行分離,使得同樣的構建過程可以創建不同的表示。
1.2、用構造者模式實現ImageLoader。
首先先簡單分析下自己如果要實現一個ImageLoader需要具備那部分。
示例只對ImageLoader需要的緩存機制和下載器做一些配置。
ImageLoader是一個高度拓展的一個圖片框架,最為突出的一點就是,ImageLoader中的組件很大部分是可以自己定制的,只要符合ImageLoader定義的接口規范即可。
定義一個ImageLoaderConfig來作為配置ImageLoader參數的類。
package com.axe.builder.imageloader;
/**
* 圖片下載框架的配置
*
* @author 11373
*
*/
public class ImageLoaderConfig {
/**
* 使用內存緩存
*/
public boolean useMemoryCache = false;
/**
* 使用自定義的內存緩存
*/
public boolean useCustomMemoryCache = false;
/**
* 使用磁盤緩存
*/
public boolean useDiskCache = false;
/**
* 使用自定義的磁盤緩存
*/
public boolean useCustomDiskCache = false;
/**
* 是否使用自定義的下載器
*/
public boolean useCustomDownLoader = false;
/**
* 內存緩存
*/
public Cache memoryCache;
/**
* 磁盤緩存
*/
public Cache diskCache;
/**
* 下載器
*/
public DownLoader downLoader;
/**
* 構造者
*
* @author 11373
*
*/
public static class Builder {
/**
* 內存緩存
*/
public Cache memoryCache;
/**
* 磁盤緩存
*/
public Cache diskCache;
/**
* 下載器
*/
public DownLoader downLoader;
public boolean useMemoryCache = false;
public boolean useDiskCache = false;
public Builder setDownLoader(DownLoader downLoader) {
this.downLoader = downLoader;
return this;
}
public Builder setMemoryCache(Cache memoryCache) {
this.memoryCache = memoryCache;
return this;
}
public Builder setDiskCache(Cache diskCache) {
this.diskCache = diskCache;
return this;
}
public Builder userMemoryCache(boolean flag) {
this.useMemoryCache = flag;
return this;
}
public Builder useDiskCache(boolean flag) {
this.useDiskCache = flag;
return this;
}
public void applyConfig(ImageLoaderConfig config) {
if (useMemoryCache) {
config.useMemoryCache = true;
if (memoryCache != null) {
config.memoryCache = memoryCache;
} else {
// 如果外部沒有傳入自己的內存緩存策略,則使用默認的
config.memoryCache = new MemoryCache();
}
}
if (useDiskCache) {
config.useDiskCache = true;
if (diskCache != null) {
config.diskCache = diskCache;
} else {
// 如果外部沒有傳入自己的內存緩存策略,則使用默認的
config.diskCache = new DiskCache();
}
}
if (downLoader != null) {
config.downLoader = downLoader;
config.useCustomDownLoader = true;
} else {
// 如果外部沒有傳入自己的內存緩存,則使用默認的
config.downLoader = new DeFaultDownLoader();
}
}
public ImageLoaderConfig create() {
ImageLoaderConfig config = new ImageLoaderConfig();
applyConfig(config);
return config;
}
}
}
而ImageLoader這個類只用于下載圖片,它的核心方法是displayImage,然后他的所有配置都來自ImageLoaderConfig:
package com.axe.builder.imageloader;
/**
* 圖片下載器
*
* @author 11373
*
*/
public class ImageLoader {
private ImageLoaderConfig config;
private static ImageLoader imageLoader;
private ImageLoader() {
}
public static ImageLoader getInstance() {
if (imageLoader == null) {
synchronized (ImageLoader.class) {
if (imageLoader == null) {
imageLoader = new ImageLoader();
}
}
}
return imageLoader;
}
// 初始化
public void init(ImageLoaderConfig config) {
this.config = config;
}
public void displayImage(String url, ImageView image) {
Bitmap bitmap = null;
// 如果設置了內存緩存,就使用內存緩存。
if (bitmap == null && config.useMemoryCache) {
bitmap = config.memoryCache.getBitmap(url);
}
// 設置了磁盤緩存,就是使用磁盤緩存。
if (bitmap == null && config.useDiskCache) {
bitmap = config.diskCache.getBitmap(url);
}
// 如果內存和磁盤都找不到圖片,那就去下載
if (bitmap == null) {
config.downLoader.downloadImage(url);
}
image.setBitmap(bitmap);
}
}
初始化和使用:
public class BuilderMain {
public static void main(String[] args) {
ImageLoaderConfig config = new Builder()
.setDiskCache(new AxeDiskCache())
.setMemoryCache(new AxeMemoryCache())
.useDiskCache(true)
.userMemoryCache(true)
.setDownLoader(new AxeImageLoader())
.create();
ImageLoader loader = ImageLoader.getInstance();
loader.init(config);
// 使用
loader.displayImage("", null);
}
}
1.3、構造者模式的優缺點
優點:良好的封裝性,使用構造者模式可以使客戶端不必知道產品內部的組成細節。構造者獨立存在,便于拓展。
缺點:會產生多余的構造者對象,消耗內存。
2、工廠模式
2.1、工廠方法模式
這個簡單的來說,就是面向對象的多態實現,建一個創建對象的接口,具體創建什么接口由子類自己決定。就相當于一個有一個創建汽車的工廠,具體有寶馬汽車工廠和奔馳汽車工廠實現了它,具體是造出寶馬車還是奔馳車由這兩個方法決定。
- 創建一個汽車的實現類,封裝了汽車的公共方法,開車。
public interface Car {
void drive();
}
public class BenziCar implements Car {
@Override
public void drive() {
System.out.println("駕駛奔馳車");
}
}
public class BWMCar implements Car{
@Override
public void drive() {
System.out.println("寶馬車在駕駛");
}
}
- 創建一個公共的方法,都有一個公共的屬性就是創建汽車。然后奔馳工廠和寶馬工廠都會繼承它,并且都有一個公共的方法,那就是創建汽車,也就是都有一個創建對象的方法。
public interface Factory {
Car createCar();
}
public class BenziFactory implements Factory{
@Override
public Car createCar() {
return new BenziCar();
}
}
public class BWMCarFactory implements Factory {
@Override
public Car createCar() {
return new BWMCar();
}
}
實際使用:
public class FactoryMain {
public static void main(String[] args) {
Factory benziFactory = new BenziFactory();
benziFactory.createCar().drive();
Factory bwmFactory = new BWMCarFactory();
bwmFactory.createCar().drive();
}
}
駕駛奔馳車
寶馬車在駕駛
工廠方法模式的優缺點
- 優點:符合開放封閉原則。新增產品時,只需增加相應的具體產品類和相應的工廠子類即可;符合單一職責原則。每個具體工廠類只負責創建對應的產品。(本段文字來自博客:Android的設計模式-工廠方法模式)
- 缺點:一個具體工廠只能創建一種具體產品;增加新產品時,還需增加相應的工廠類,系統類的個數將成對增加,增加了系統的復雜度和性能開銷;引入的抽象類也會導致類結構的復雜化。(本段文字來自博客:Android的設計模式-工廠方法模式)
2.2、簡單工廠模式
繼續2.1的例子,我們將所有的實現的汽車工廠變成一個工廠,這個工廠可以生成各種不同的汽車。修改汽車工廠的基類如下:
public interface Factory2 {
Car createCar(String name);
}
我們只要傳入我們想生成的汽車名字,我們即可生成改汽車的對象。
實現類如下:
public class CarFactory implements Factory2 {
@Override
public Car createCar(String name) {
if ("benzi".equals(name)) {
return new BenziCar();
} else if ("bwm".equals(name)) {
return new BWMCar();
}
return null;
}
}
實際使用時:
Factory2 factory2 = new CarFactory();
factory2.createCar("benzi").drive();
factory2.createCar("bwm").drive();
還有一種用方式的去實現這個公用的工廠:
public class CarFactory2 {
public static <T extends Car> T createCar(Class<T> clz) {
Car car = null;
try {
car = (Car) Class.forName(clz.getName()).newInstance();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return (T) car;
}
}
實際使用時:
CarFactory2 factory3 = new CarFactory2();
factory3.createCar(BenziCar.class).drive();
factory3.createCar(BWMCar.class).drive();
簡單工廠模式的優缺點
- 優點:代碼解耦,創建實例的工作與使用實例的工作分開,使用者不必關心類對象如何創建。
- 缺點:從第一種實現方式就可以看到,會增加很多的if判斷,當要產生的對象變多的時候不便于維護;違背開放封閉原則,若需添加新產品則必須修改工廠類邏輯,會造成工廠邏輯過于復雜;簡單工廠模式使用了靜態工廠方法,因此靜態方法不能被繼承和重寫;工廠類包含了所有實例(產品)的創建邏輯,若工廠類出錯,則會造成整個系統都會會受到影響。(本段文字來自博客:Android的設計模式-工廠方法模式)
2.3、抽象工廠模式
現在造一輛車的一些零件肯定是由各種不同的廠商生產的。寶馬汽車和奔馳汽車的引擎和輪子也不是同一個廠商生產的,那么如果將所有的這些零件工廠也抽象出來,代碼表示如下:
零件的接口:
public interface Engine {
void getEngine();
}
public interface Wheel {
void getWheel();
}
零件的實現類:
public class AKMWheel implements Wheel{
@Override
public void getWheel() {
System.out.println("AKM輪子");
}
}
public class M416Wheel implements Wheel{
@Override
public void getWheel() {
System.out.println("M426輪子");
}
}
public class M24Engine implements Engine {
@Override
public void getEngine() {
System.out.println("M24引擎");
}
}
public class AWMEngine implements Engine {
@Override
public void getEngine() {
System.out.println("AWM引擎");
}
}
創建汽車的接口:
public interface Factory3 {
Wheel createWheel();
Engine createEngine();
}
寶馬汽車和奔馳汽車的具體實現:
public class BenziCarFactory implements Factory3 {
@Override
public Wheel createWheel() {
return new AKMWheel();
}
@Override
public Engine createEngine() {
return new M24Engine();
}
}
public class BWMCarsFactory implements Factory3{
@Override
public Wheel createWheel() {
return new M416Wheel();
}
@Override
public Engine createEngine() {
return new AWMEngine();
}
}
具體使用:
System.out.println("創建寶馬車");
Factory3 bwFactory = new BWMCarsFactory();
bwFactory.createEngine().getEngine();
bwFactory.createWheel().getWheel();
System.out.println("創建奔馳車");
Factory3 bzFactory = new BenziCarFactory();
bzFactory.createEngine().getEngine();
bzFactory.createWheel().getWheel();
創建寶馬車
AWM引擎
M426輪子
創建奔馳車
M24引擎
AKM輪子
抽象工廠模式的優缺點
- 優點:代碼解耦,創建實例的工作與使用實例的工作分開,使用者不必關心類對象如何創建。
- 缺點: 如果增加新的產品,則修改抽象工廠和所有的具體工廠,違反了開放封閉原則(本段文字來自博客:Android的設計模式-工廠方法模式)
3、策略模式
繼續用英雄聯盟來舉例。
在這個游戲中獲取賞金的方式有很多種,比如:擊殺野怪、擊殺英雄、擊殺小兵等等。如果讓我寫一個計算賞金的方法,我會怎么寫呢。
package com.axe.strategy;
/**
* 賞金類
*
* @author 11373
*
*/
public class Bounty {
/**
* 小兵
*/
public static final int TYPE_BATMAM = 0;
/**
* 英雄
*/
public static final int TYPE_HERO = 1;
/**
* 野怪
*/
public static final int TYPE_MONSTER = 2;
/**
* 大龍
*/
public static final int TYPE_DRAGON = 3;
/**
* 獲取賞金的方法
*
* @param type 類型
* @param count 數量
* @return
*/
public int killGetMoney(int type, int count) {
int sum = 0;
if (type == TYPE_BATMAM) {
sum += getBatmanMoney(count);
} else if (type == TYPE_HERO) {
sum += getHeroMoney(count);
} else if (type == TYPE_MONSTER) {
sum += getMonsterMoney(count);
} else if (type == TYPE_DRAGON) {
sum += getDragonMoney(count);
}
return sum;
}
/**
* 獲取小兵的錢
*
* @param count
* @return
*/
private int getBatmanMoney(int count) {
return count * 30;
}
/**
* 獲取英雄的錢
*
* @param count
* @return
*/
private int getHeroMoney(int count) {
return count * 300;
}
/**
* 獲取野怪的錢
*
* @param count
* @return
*/
private int getMonsterMoney(int count) {
return count * 50;
}
/**
* 獲取大龍的錢
* @param count
* @return
*/
private int getDragonMoney(int count) {
return count * 500;
}
}
在實際使用的時候:
Bounty bounty = new Bounty();
int getMoney = bounty.killGetMoney(Bounty.TYPE_BATMAM, 10) + bounty.killGetMoney(Bounty.TYPE_HERO, 1)
+ bounty.killGetMoney(Bounty.TYPE_MONSTER, 6);
看起來問題不大,也比較簡潔。但是如果這個是一個真實的英雄聯盟中的例子,那么情況會有這么簡單嗎?比如:英雄聯盟中的擊殺野怪,野怪有很多種,獲得的賞金也不一樣;擊殺英雄的時候,不一定就是300個金幣。還有某些輔助裝備會導致金幣不一樣。而且這里覆蓋的擊殺種類肯定不止這么多。比如推掉防御塔也有金幣,推掉水晶也有金幣等等。如果按照所有的情況去計算的話,Bounty 這個類將會非常龐大,里面的邏輯判斷也會非常多。
/**
* 獲取賞金的方法
*
* @param type 類型
* @param count 數量
* @return
*/
public int killGetMoney(int type, int count) {
int sum = 0;
if (type == TYPE_BATMAM) {
sum += getBatmanMoney(count);
} else if (type == TYPE_HERO) {
sum += getHeroMoney(count);
} else if (type == TYPE_MONSTER) {
sum += getMonsterMoney(count);
} else if (type == TYPE_DRAGON) {
sum += getDragonMoney(count);
}
return sum;
}
這個方法的if嵌套層數會非常多。if條件太多維護起來絕對是痛苦,并且會帶來更多的錯誤!如果使用策略模式去優化的話,如何去寫呢?
- 定義要給獲取賞金的規則
/**
* 獲取賞金的規則
* @author 11373
*
*/
public interface GetMoney {
public int getMoney(int count);
}
- 將每種類型的獲取賞金的方法繼承該接口
/**
* 小兵賞金計算器
* @author 11373
*
*/
public class BatmanCalculater implements GetMoney {
@Override
public int getMoney(int count) {
return count * 30;
}
}
- 定義一個類去控制賞金獲取的方式
/**
* 賞金計算器
*
* @author 11373
*
*/
public class BountyCalculater {
private GetMoney getMoney;
public int getBountyMoney(int count) {
return getMoney.getMoney(count);
}
public void setGetMoney(GetMoney getMoney) {
this.getMoney = getMoney;
}
}
實際的使用情況。遇到不同的獲取賞金的情況時,只需要替換不同的計算規則即可。
// 使用策略模式
int sum = 0;
BountyCalculater calculater = new BountyCalculater();
calculater.setGetMoney(new BatmanCalculater());
sum += calculater.getBountyMoney(10);
calculater.setGetMoney(new HeroMoneyCalculater());
sum += calculater.getBountyMoney(5);
calculater.setGetMoney(new DragonCalculater());
sum += calculater.getBountyMoney(1);
calculater.setGetMoney(new MonsterCalculater());
sum += calculater.getBountyMoney(34);
System.out.println(sum);
現在看來應該是增加了不少的類,這個就是策略模式的一個劣勢。
但是如果從類的單一性原則和產品可維護階段來說,就會感覺這個策略模式的妙用之處。
- 類的單一性原則
每個種類的計算規則都單獨封裝成一套計算方法,修改了某一套計算方法不會對其他的計算規則產生影響。 - 可維護和拓展
假如要新增一個計算規則,只需要繼承GetMoney即可,不會對其他的計算規則產生任何的影響。
3.1、策略模式的優缺點
優點:結構清晰明了,使用簡單直觀;耦合度比較低,便于拓展;
缺點:隨著策略的增多,策略的子類也會增多。
4、單例模式
這種是比較常見的模式了,如果是封裝什么網絡請求框架、圖片請求框架,在整個app只需要一個全局對象的時候都會用到這個模式。很多博客應該已經把這個東西介紹得很清楚,單例模式的意義就是——讓整個程序中只有唯一的一個對象。
4.1、餓漢模式
/**
* Created by Axe on 2017/8/29.
* <p>
* 單例模式 - 餓漢模式
* 最簡單的單例模式,具有單利模式的所有特征
* 缺點:
* 1、當類加載時就會初始化成員變量,可能會浪費資源。
* 2、在多線程情況下,不安全,無法保證對象唯一。
*/
public class HungryModeSingleton {
private static final HungryModeSingleton singleton = new HungryModeSingleton();
private HungryModeSingleton() {
}
public static HungryModeSingleton getInstance() {
return singleton;
}
}
4.2、懶漢模式
/**
* Created by Axe on 2017/8/29.
* 1、懶漢模式只有調用的時候才初始化,節省了開支。
* 2、懶漢模式保證了線程安全
* 缺點:
* 每次初始化會進行同步,會消耗不必要的資源
*/
public class LazyModeSingleton {
private static LazyModeSingleton singleton;
private LazyModeSingleton() {
}
public static synchronized LazyModeSingleton getInstance() {
if (singleton == null) {
singleton = new LazyModeSingleton();
}
return singleton;
}
}
4.3、雙層檢測通道模式(線程安全)
/**
* Created by Axe on 2017/8/29.
* <p>
* Double Check Lock (雙層檢查同步模式)
*/
public class DCLSingleton {
private static DCLSingleton singleton = null;
private DCLSingleton() {
}
public static DCLSingleton getInstance() {
if (singleton == null) {
synchronized (DCLSingleton.class) {
if (singleton == null) {
singleton = new DCLSingleton();
}
}
}
return singleton;
}
}
4.4、內部類模式
/**
* Created by Axe on 2017/8/29.
* <p>
* 靜態內部類單例模式
*/
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton() {
}
public static StaticInnerClassSingleton getInstance() {
return SingletonHolder.singleton;
}
private static class SingletonHolder {
private static final StaticInnerClassSingleton singleton = new StaticInnerClassSingleton();
}
}
##### 4.5單例模式的優缺點
優點:整個內存中只有一個對象,減少了內存消耗,減少了系統性能的開銷。
缺點:持有Context時,可能導致內存泄漏;擴展比較困難,只能修改源代碼來修改拓展。
5、狀態模式
一看到狀態模式,就能想到,這個肯定是和”狀態“有關。
繼續用打英雄聯盟舉例(今天和它杠上了):
現在生活中有一個這樣的情況,如果要打英雄聯盟,那電腦就必須開機,此時的電腦的狀態為NO,如果電腦沒開機就無法玩英雄聯盟,此時的電腦狀態為OFF。如果現在去寫一個玩英雄聯盟但是依賴電腦狀態的類,如果電腦開機就可以玩游戲,電腦沒有開機就不玩游戲,并提示開機才能玩游戲,那該如何寫呢?
在沒有學習狀態模式的時候,我是這么寫的:
public class PlayGameController implements IComputerActivity {
/**
* 表示關機狀態
*/
private static final int OFF = 0;
/**
* 表示開機狀態
*/
private static final int NO = 1;
private int state = 0;
public void setSate(int state) {
this.state = state;
}
@Override
public void playGame() {
if (state == OFF) {
System.out.println("請先開機再打游戲");
} else {
System.out.println("正在打游戲");
}
}
@Override
public void watchMovie() {
if (state == OFF) {
System.out.println("請先開機再看電影");
} else {
System.out.println("正在看電影");
}
}
}
這里定義了電腦的一些行為接口IComputerActivity ,這些電腦暫時有兩個方法:playGame和watchMovie。
打游戲和看電影都必須在開機之后才能執行的操作,所以這里在這兩個方法中都加了判斷電腦是不是開機的狀態:
if (state == OFF) {
System.out.println("請先開機再打游戲");
} else {
System.out.println("正在打游戲");
}
在狀態比較少和電腦的行為比較少時問題不大。但是假如電腦的狀態變多,這個if條件將會變得很繁瑣,比如if判斷會變成這樣:
if (state == OFF) {
System.out.println("請先開機再打游戲");
} else if(state == xxx){
System.out.println("正在打游戲");
}else if(state == rrr){
}
... ... 此處省略若干if條件
假如電腦的行為不僅僅是玩游戲和看電影,還有數個行為的話,那這些if判斷每個行為中都要寫一遍。重復的if判斷維護起來也非常麻煩,也更加容易出錯。那有沒有辦法讓這些行為能單獨處理,一個類只處理一個狀態?
那么用狀態模式來重構這些代碼。
1、電腦開機狀態的處理:
public class PowerNoState implements IComputerActivity{
@Override
public void playGame() {
System.out.println("正在打英雄聯盟");
}
@Override
public void watchMovie() {
System.out.println("正在看火影忍者");
}
}
2、電腦關機狀態的行為處理:
/**
* 狀態模式 :當電腦電源關閉之后的操作
* @author 11373
*
*/
public class PowerOffState implements IComputerActivity{
@Override
public void playGame() {
System.out.println("請開機玩游戲");
}
@Override
public void watchMovie() {
System.out.println("請開機看電影");
}
}
3、然后定義好電腦電源
public interface PowerController {
public void powerOn();
public void powerOff();
}
當調用powerOn時我們就初始化PowerNoState,當調用powerOff就初始化PowerOffState這樣
5.1、狀態模式的優缺點
優點:將每一個狀態單獨封裝成子類,便于維護和拓展;能減少過多的條件語句,使結構更加清晰,提高代碼的維護性。
缺點:當狀態很多時,必然會增加狀態子類的個數。
6、觀察者模式
如果用過RxJava就會接觸到觀察者模式了。定義對象的一種一多的依賴關系,則所有的依賴于它的對象都會得到通知并且自動更新。
舉一個游戲中的簡單的例子,比如在英雄聯盟中,易大師穿著復活甲被殺死了,這個時候蓋倫和艾希都在等待易大師復活,再對他進行攻擊。
那么,這里的觀察者就是蓋倫和艾希。他們有一個共同的行為就是打擊的操作。
/**
* 觀察者
* @author 11373
*
*/
public interface Observer {
/**
* 每一個觀察者都有一個攻擊的方法
* @param name
*/
public void hit(String name);
}
那易大師能被其他人觀察到它的狀態,并當他發生狀態改變的時候進行改變。
/**
* 被觀察類
*
* @author 11373
*
*/
public interface Obserable {
//提供的能被觀察者觀察到的方法
public void addObserver(Observer observer);
public void removeObserver(Observer observer);
// 英雄復活的方法
public void resurgence(String name);
}
蓋倫的實現:
/**
* 觀察者實現類
* @author 11373
*
*/
public class GaLenObserverImpl implements Observer{
@Override
public void hit(String name) {
System.out.println("我是蓋倫,"+name+"復活了,快打他");
}
}
易大師的實現:
/**
* 易大師,被觀察者
*
* @author 11373
*
*/
public class YiObserableImpl implements Obserable {
private List<Observer> observers = new ArrayList<>();
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
if (observers.contains(observer)) {
observers.remove(observer);
}
}
@Override
public void resurgence(String name) {
for (Observer observer : observers) {
observer.hit(name);
}
}
}
最后的執行效果:
public static void main(String[] args) {
Observer aich = new AichObserverlmpl();
Observer galen = new GaLenObserverImpl();
Obserable yi = new YiObserableImpl();
yi.addObserver(aich);
yi.addObserver(galen);
// 易大師復活的行為
yi.resurgence("易大師");
}
我是艾希易大師復活了,快打他
我是蓋倫易大師復活了,快打他
6.1、觀察者模式的優缺點
優點:觀察者對象和被觀察者對象解耦,雙方依賴都依賴抽象,而不是依賴具體對象。
缺點:依賴關系并未完全解除,抽象主題任然依賴抽象觀察者;使用觀察者模式時需要考慮一下開發效率和運行效率的問題,程序中包括一個被觀察者、多個觀察者,開發、調試等內容會比較復雜,而且在Java中消息的通知一般是順序執行,那么一個觀察者卡頓,會影響整體的執行效率,在這種情況下,一般會采用異步實現。
7、代理模式
在玩游戲的時候,通常有這種情況發生。有些人玩游戲技術并不咋地,但是段位卻老高。以上就是常見的代理模式啦!
把以上的情況變成代碼該如何表示呢?
定義玩家接口:
* 游戲玩家接口
*
* @author 11373
*
*/
public interface IPlay {
/**
* 登錄游戲
*/
void loginGame();
/**
* 打游戲
*/
void play();
/**
* 贏得比賽
*/
void winGame();
}
定義普通玩家接口:
/**
* 游戲玩家類,實際要贏得游戲的玩家
* @author 11373
*
*/
public class GamePlayer implements IPlay{
@Override
public void loginGame() {
System.out.println("游戲玩家登錄游戲");
}
@Override
public void play() {
System.out.println("游戲玩家開始打游戲");
}
@Override
public void winGame() {
System.out.println("游戲玩家贏得了比賽");
}
}
定義代打接口:
/**
* 靠代打游戲生存的游戲代打
*
* @author 11373
*
*/
public class PlayerProxy implements IPlay {
private IPlay player;
public void setIPlyer(IPlay player) {
this.player = player;
}
@Override
public void loginGame() {
player.loginGame();
}
@Override
public void play() {
player.play();
}
@Override
public void winGame() {
player.winGame();
}
}
實際操作:
public static void main(String[] args) {
// 實際要打游戲的游戲玩家
GamePlayer axeChen = new GamePlayer();
// 游戲代打
PlayerProxy gameProxy = new PlayerProxy();
// 游戲代打知道要為誰代打游戲
gameProxy.setIPlyer(axeChen);
// 代打開始登錄游戲
gameProxy.loginGame();
// 代打開始打游戲
gameProxy.play();
// 代打贏得了比賽
gameProxy.winGame();
}
7.1、代理模式的優缺點
推薦看下這篇博客的總結,http://www.lxweimin.com/p/a0e687e0904f
8、適配器模式
適配器模式在安卓開發經常可以見到,RecyclerView的setAdapter就是典型的適配器模式,將數據源傳入,然后再適配不同的UI布局。
一個簡單的例子去說明適配器模式:
比如生活中的手機電源,我們的電源通常是220v,但是手機上能接受的電源只有5V。這個是我們通常有一個手機的充電適配器去將220v的電壓轉為5v。接下來把這種情況變成代碼。
定義適配器接口:
public interface Adapter {
public int getVolt5();
}
電源實體類(實際輸出電源):
public class Power {
public int get220v() {
return 220;
}
}
電源適配器類,關鍵的適配操作:
public class PhoneAdapter implements Adapter {
private Power power;
public PhoneAdapter(Power power) {
this.power = power;
}
@Override
public int getVolt5() {
return 5;
}
}
代碼測試:
Power power = new Power();
System.out.println("電源電壓:"+power.get220v());
PhoneAdapter adapter = new PhoneAdapter(power);
System.out.println("通過是配置適配之后的電壓:"+adapter.getVolt5());
輸出結果:
電源電壓:220
通過是配置適配之后的電壓:5
以上的代碼就是將輸入的電壓220v通過適配器轉化成5v。RecyclerView的Adapter就是將數據源傳入適配器(adapter)中,然后去適配不同的布局。
當然這邊還有一種類適配器模式這邊這邊簡單提下:
public class PhoneAdapter2 extends Power implements Adapter {
@Override
public int getVolt5() {
System.out.println("電源電壓:"+get220v());
System.out.println("經過適配器適配后的電壓:"+5);
return 5;
}
}
這邊是用適配器繼承數據源,同時實現適配器的接口。它的優勢是無需持有數據源對象,只需繼承數據源對象。
8.1、適配器模式的優缺點
優點:提高了類的復用性,適配器能讓一個類有更廣泛的用途;提高了靈活性,更換適配器就能達到不同的效果。不用時也可以隨時刪掉適配器,對原系統沒影響。
缺點:過多的使用適配器,會讓系統非常零亂,不易整體進行把握。明明調用A接口,卻被適配成B接口。
9、責任鏈模式
以生活中的一個例子來解釋責任鏈模式。比如公司的一個員工購買了一個辦公用品,一共花費5000元,這個時候該員工去找部門經理審批報銷,部門經理一看5000元已經大于他能審批的金額,于是就交給總監去審批。總監一看5000元也大于他能報銷的金額,于是就交給老板去審批,老板能報銷員工10w以內的金額,于是老板審批通過,同意了報銷。
以上就是一個簡單的責任鏈模式的例子,他的定義為:****
9.1、用代碼來解釋報銷的案例
所有領導的相同點抽象,他們都能報銷,有報銷金額的范圍等等。
public abstract class Leader {
/**
* 下一個執行者
*/
public Leader nextHanlder;
/**
* 自身能夠處理的最少金額
*
* @return
*/
public abstract int limit();
/**
* 報銷金額的方法
*
* @param money
*/
public abstract void handle(int money);
/**
* 控制責任鏈的條件
*
* @param money
*/
public final void handleRequest(int money) {
if (money <= limit()) {
handle(money);
} else {
nextHanlder.handleRequest(money);
}
}
}
然后部門經理,總監,CEO都實現了這些方法。
/**
* 經理級別最多報銷1000
*
* @author 11373
*
*/
public class Manager extends Leader {
@Override
public int limit() {
return 1000;
}
@Override
public void handle(int money) {
System.out.println("經理正在處理報銷金額:"+money);
}
}
public class CTO extends Leader{
@Override
public int limit() {
return 5000;
}
@Override
public void handle(int money) {
System.out.println("CTO正在處理報銷金額:"+money);
}
}
public class CEO extends Leader{
@Override
public int limit() {
return 100000;
}
@Override
public void handle(int money) {
System.out.println("CEO正在處理報銷金額:"+money);
}
}
最后測試:
public static void main(String[] args) {
CEO ceo = new CEO();
CTO cto = new CTO();
Manager manager = new Manager();
manager.nextHanlder = cto;
cto.nextHanlder = ceo;
manager.handleRequest(5000);
}
這里的執行結果是,經理無法報銷,CTO能報銷
于是CTO就報銷了這筆金額。
CTO正在處理報銷金額:5000
9.2、責任鏈模式的優缺點
優點:請求者和處理者關系解耦,處理者比較好的擴展。
缺點:處理者太多會影響性能,特別是循環遞歸的時候。
10、原型模式
原型模式的核心為clone方法,涉及java中的深拷貝和淺拷貝的知識。
這邊關于深拷貝和淺拷貝的東西,涉及的東西比較多,這里就直接引用別人的博客吧.
http://www.lxweimin.com/p/6d1333917ae5
參考書籍:《Android源碼設計模式,解析與實戰》
參考博客:http://www.lxweimin.com/p/bf92927c9d22
感謝博主四月葡萄的博客,他寫的博客總結得比較好,建議去看看!我很多地方也是引用他寫的鏈接。
代碼地址:https://github.com/AxeChen/DesignMode