新聞中心
設(shè)計模式是眾多軟件開發(fā)人員經(jīng)過長時間的試錯和應(yīng)用總結(jié)出來的,解決特定問題的一系列方案?,F(xiàn)行的部分教材在介紹設(shè)計模式時,有些會因為案例脫離實際應(yīng)用場景而令人費解,有些又會因為場景簡單而顯得有些小題大做。

站在用戶的角度思考問題,與客戶深入溝通,找到瑞金網(wǎng)站設(shè)計與瑞金網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗,讓設(shè)計與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個性化、用戶體驗好的作品,建站類型包括:成都網(wǎng)站設(shè)計、成都網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機端網(wǎng)站、網(wǎng)站推廣、空間域名、虛擬空間、企業(yè)郵箱。業(yè)務(wù)覆蓋瑞金地區(qū)。
本文會根據(jù)在美團金融服務(wù)平臺設(shè)計開發(fā)時的經(jīng)驗,結(jié)合實際的案例,并采用“師生對話”這種相對詼諧的形式去講解幾類常用設(shè)計模式的應(yīng)用。希望能對想提升系統(tǒng)設(shè)計能力的同學(xué)有所幫助或啟發(fā)。
引言
話說這是在程序員世界里一對師徒的對話:“老師,我最近在寫代碼時總感覺自己的代碼很不優(yōu)雅,有什么辦法能優(yōu)化嗎?”“嗯,可以考慮通過教材系統(tǒng)學(xué)習(xí),從注釋、命名、方法和異常等多方面實現(xiàn)整潔代碼?!薄叭欢蚁胝f的是,我的代碼是符合各種編碼規(guī)范的,但是從實現(xiàn)上卻總是感覺不夠簡潔,而且總是需要反復(fù)修改!”學(xué)生小明嘆氣道。老師看了看小明的代碼說:“我明白了,這是系統(tǒng)設(shè)計上的缺陷??偨Y(jié)就是抽象不夠、可讀性低、不夠健壯?!薄皩?,那怎么能迅速提高代碼的可讀性、健壯性、擴展性呢?”小明急不可耐地問道。老師敲了敲小明的頭:“不要太浮躁,沒有什么方法能讓你立刻成為系統(tǒng)設(shè)計專家。但是對于你的問題,我想設(shè)計模式可以幫到你?!薄霸O(shè)計模式?”小明不解?!笆堑摹!崩蠋燑c了點頭,“世上本沒有路,走的人多了,便變成了路。在程序員的世界中,本沒有設(shè)計模式,寫代碼是人多了,他們便總結(jié)出了一套能提高開發(fā)和維護效率的套路,這就是設(shè)計模式。設(shè)計模式不是什么教條或者范式,它可以說是一種在特定場景下普適且可復(fù)用的解決方案,是一種可以用于提高代碼可讀性、可擴展性、可維護性和可測性的最佳實踐?!薄芭杜?,我懂了,那我應(yīng)該如何去學(xué)習(xí)呢?”“不急,接下來我來帶你慢慢了解設(shè)計模式?!?/p>
獎勵的發(fā)放策略
第一天,老師問小明:“你知道活動營銷嗎?”“這我知道,活動營銷是指企業(yè)通過參與社會關(guān)注度高的已有活動,或整合有效的資源自主策劃大型活動,從而迅速提高企業(yè)及其品牌的知名度、美譽度和影響力,常見的比如有抽獎、紅包等。”老師點點頭:“是的。我們假設(shè)現(xiàn)在就要做一個營銷,需要用戶參與一個活動,然后完成一系列的任務(wù),最后可以得到一些獎勵作為回報?;顒拥莫剟畎缊F外賣、酒旅和美食等多種品類券,現(xiàn)在需要你幫忙設(shè)計一套獎勵發(fā)放方案。”因為之前有過類似的開發(fā)經(jīng)驗,拿到需求的小明二話不說開始了編寫起了代碼:
// 獎勵服務(wù)
class RewardService {
// 外部服務(wù)
private WaimaiService waimaiService;
private HotelService hotelService;
private FoodService foodService;
// 使用對入?yún)⒌臈l件判斷進行發(fā)獎
public void issueReward(String rewardType, Object ... params) {
if ("Waimai".equals(rewardType)) {
WaimaiRequest request = new WaimaiRequest();
// 構(gòu)建入?yún)?br> request.setWaimaiReq(params);
waimaiService.issueWaimai(request);
} else if ("Hotel".equals(rewardType)) {
HotelRequest request = new HotelRequest();
request.addHotelReq(params);
hotelService.sendPrize(request);
} else if ("Food".equals(rewardType)) {
FoodRequest request = new FoodRequest(params);
foodService.getCoupon(request);
} else {
throw new IllegalArgumentException("rewardType error!");
}
}
}
// 獎勵服務(wù)
class RewardService {
// 外部服務(wù)
private WaimaiService waimaiService;
private HotelService hotelService;
private FoodService foodService;
// 使用對入?yún)⒌臈l件判斷進行發(fā)獎
public void issueReward(String rewardType, Object ... params) {
if ("Waimai".equals(rewardType)) {
WaimaiRequest request = new WaimaiRequest();
// 構(gòu)建入?yún)?br> request.setWaimaiReq(params);
waimaiService.issueWaimai(request);
} else if ("Hotel".equals(rewardType)) {
HotelRequest request = new HotelRequest();
request.addHotelReq(params);
hotelService.sendPrize(request);
} else if ("Food".equals(rewardType)) {
FoodRequest request = new FoodRequest(params);
foodService.getCoupon(request);
} else {
throw new IllegalArgumentException("rewardType error!");
}
}
}
小明很快寫好了Demo,然后發(fā)給老師看?!凹偃缥覀兗磳⒔尤胄碌拇蜍嚾?,這是否意味著你必須要修改這部分代碼?”老師問道。小明愣了一愣,沒等反應(yīng)過來老師又問:”假如后面美團外賣的發(fā)券接口發(fā)生了改變或者替換,這段邏輯是否必須要同步進行修改?”小明陷入了思考之中,一時間沒法回答。經(jīng)驗豐富的老師一針見血地指出了這段設(shè)計的問題:“你這段代碼有兩個主要問題,一是不符合開閉原則,可以預(yù)見,如果后續(xù)新增品類券的話,需要直接修改主干代碼,而我們提倡代碼應(yīng)該是對修改封閉的;二是不符合迪米特法則,發(fā)獎邏輯和各個下游接口高度耦合,這導(dǎo)致接口的改變將直接影響到代碼的組織,使得代碼的可維護性降低?!毙∶骰腥淮笪颍骸澳俏覍⒏鱾€同下游接口交互的功能抽象成單獨的服務(wù),封裝其參數(shù)組裝及異常處理,使得發(fā)獎主邏輯與其解耦,是否就能更具備擴展性和可維護性?”“這是個不錯的思路。之前跟你介紹過設(shè)計模式,這個案例就可以使用策略模式和適配器模式來優(yōu)化。”小明借此機會學(xué)習(xí)了這兩個設(shè)計模式。首先是策略模式:
策略模式定義了一系列的算法,并將每一個算法封裝起來,使它們可以相互替換。策略模式通常包含以下角色:
- 抽象策略(Strategy)類:定義了一個公共接口,各種不同的算法以不同的方式實現(xiàn)這個接口,環(huán)境角色使用這個接口調(diào)用不同的算法,一般使用接口或抽象類實現(xiàn)。
- 具體策略(Concrete Strategy)類:實現(xiàn)了抽象策略定義的接口,提供具體的算法實現(xiàn)。
- 環(huán)境(Context)類:持有一個策略類的引用,最終給客戶端調(diào)用。
然后是適配器模式:
適配器模式:將一個類的接口轉(zhuǎn)換成客戶希望的另外一個接口,使得原本由于接口不兼容而不能一起工作的那些類能一起工作。適配器模式包含以下主要角色:
- 目標(biāo)(Target)接口:當(dāng)前系統(tǒng)業(yè)務(wù)所期待的接口,它可以是抽象類或接口。
- 適配者(Adaptee)類:它是被訪問和適配的現(xiàn)存組件庫中的組件接口。
- 適配器(Adapter)類:它是一個轉(zhuǎn)換器,通過繼承或引用適配者的對象,把適配者接口轉(zhuǎn)換成目標(biāo)接口,讓客戶按目標(biāo)接口的格式訪問適配者。
結(jié)合優(yōu)化思路,小明首先設(shè)計出了策略接口,并通過適配器的思想將各個下游接口類適配成策略類:
// 策略接口
interface Strategy {
void issue(Object ... params);
}
// 外賣策略
class Waimai implements Strategy {
private WaimaiService waimaiService;
@Override
public void issue(Object... params) {
WaimaiRequest request = new WaimaiRequest();
// 構(gòu)建入?yún)?br> request.setWaimaiReq(params);
waimaiService.issueWaimai(request);
}
}
// 酒旅策略
class Hotel implements Strategy {
private HotelService hotelService;
@Override
public void issue(Object... params) {
HotelRequest request = new HotelRequest();
request.addHotelReq(params);
hotelService.sendPrize(request);
}
}
// 美食策略
class Food implements Strategy {
private FoodService foodService;
@Override
public void issue(Object... params) {
FoodRequest request = new FoodRequest(params);
foodService.payCoupon(request);
}
}
// 策略接口
interface Strategy {
void issue(Object ... params);
}
// 外賣策略
class Waimai implements Strategy {
private WaimaiService waimaiService;
@Override
public void issue(Object... params) {
WaimaiRequest request = new WaimaiRequest();
// 構(gòu)建入?yún)?br> request.setWaimaiReq(params);
waimaiService.issueWaimai(request);
}
}
// 酒旅策略
class Hotel implements Strategy {
private HotelService hotelService;
@Override
public void issue(Object... params) {
HotelRequest request = new HotelRequest();
request.addHotelReq(params);
hotelService.sendPrize(request);
}
}
// 美食策略
class Food implements Strategy {
private FoodService foodService;
@Override
public void issue(Object... params) {
FoodRequest request = new FoodRequest(params);
foodService.payCoupon(request);
}
}
然后,小明創(chuàng)建策略模式的環(huán)境類,并供獎勵服務(wù)調(diào)用:
// 使用分支判斷獲取的策略上下文
class StrategyContext {
public static Strategy getStrategy(String rewardType) {
switch (rewardType) {
case "Waimai":
return new Waimai();
case "Hotel":
return new Hotel();
case "Food":
return new Food();
default:
throw new IllegalArgumentException("rewardType error!");
}
}
}
// 優(yōu)化后的策略服務(wù)
class RewardService {
public void issueReward(String rewardType, Object ... params) {
Strategy strategy = StrategyContext.getStrategy(rewardType);
strategy.issue(params);
}
}
// 使用分支判斷獲取的策略上下文
class StrategyContext {
public static Strategy getStrategy(String rewardType) {
switch (rewardType) {
case "Waimai":
return new Waimai();
case "Hotel":
return new Hotel();
case "Food":
return new Food();
default:
throw new IllegalArgumentException("rewardType error!");
}
}
}
// 優(yōu)化后的策略服務(wù)
class RewardService {
public void issueReward(String rewardType, Object ... params) {
Strategy strategy = StrategyContext.getStrategy(rewardType);
strategy.issue(params);
}
}
小明的代碼經(jīng)過優(yōu)化后,雖然結(jié)構(gòu)和設(shè)計上比之前要復(fù)雜不少,但考慮到健壯性和拓展性,還是非常值得的。“看,我這次優(yōu)化后的版本是不是很完美?”小明洋洋得意地說?!榜詈隙却_實降低了,但還能做的更好?!薄霸趺醋??”小明有點疑惑。“我問你,策略類是有狀態(tài)的模型嗎?如果不是是否可以考慮做成單例的?”“的確如此?!毙∶魉坪趺靼琢??!斑€有一點,環(huán)境類的獲取策略方法職責(zé)很明確,但是你依然沒有做到完全對修改封閉。”經(jīng)過老師的點撥,小明很快也領(lǐng)悟到了要點:“那我可以將策略類單例化以減少開銷,并實現(xiàn)自注冊的功能徹底解決分支判斷?!毙∶髁谐鰡卫J降囊c:
單例模式設(shè)計模式屬于創(chuàng)建型模式,它提供了一種創(chuàng)建對象的最佳方式。這種模式涉及到一個單一的類,該類負(fù)責(zé)創(chuàng)建自己的對象,同時確保只有單個對象被創(chuàng)建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。
最終,小明在策略環(huán)境類中使用一個注冊表來記錄各個策略類的注冊信息,并提供接口供策略類調(diào)用進行注冊。同時使用餓漢式單例模式去優(yōu)化策略類的設(shè)計:
// 策略上下文,用于管理策略的注冊和獲取
class StrategyContext {
private static final MapregisterMap = new HashMap<>();
// 注冊策略
public static void registerStrategy(String rewardType, Strategy strategy) {
registerMap.putIfAbsent(rewardType, strategy);
}
// 獲取策略
public static Strategy getStrategy(String rewardType) {
return registerMap.get(rewardType);
}
}
// 抽象策略類
abstract class AbstractStrategy implements Strategy {
// 類注冊方法
public void register() {
StrategyContext.registerStrategy(getClass().getSimpleName(), this);
}
}
// 單例外賣策略
class Waimai extends AbstractStrategy implements Strategy {
private static final Waimai instance = new Waimai();
private WaimaiService waimaiService;
private Waimai() {
register();
}
public static Waimai getInstance() {
return instance;
}
@Override
public void issue(Object... params) {
WaimaiRequest request = new WaimaiRequest();
// 構(gòu)建入?yún)?br> request.setWaimaiReq(params);
waimaiService.issueWaimai(request);
}
}
// 單例酒旅策略
class Hotel extends AbstractStrategy implements Strategy {
private static final Hotel instance = new Hotel();
private HotelService hotelService;
private Hotel() {
register();
}
public static Hotel getInstance() {
return instance;
}
@Override
public void issue(Object... params) {
HotelRequest request = new HotelRequest();
request.addHotelReq(params);
hotelService.sendPrize(request);
}
}
// 單例美食策略
class Food extends AbstractStrategy implements Strategy {
private static final Food instance = new Food();
private FoodService foodService;
private Food() {
register();
}
public static Food getInstance() {
return instance;
}
@Override
public void issue(Object... params) {
FoodRequest request = new FoodRequest(params);
foodService.payCoupon(request);
}
}
// 策略上下文,用于管理策略的注冊和獲取
class StrategyContext {
private static final MapregisterMap = new HashMap<>();
// 注冊策略
public static void registerStrategy(String rewardType, Strategy strategy) {
registerMap.putIfAbsent(rewardType, strategy);
}
// 獲取策略
public static Strategy getStrategy(String rewardType) {
return registerMap.get(rewardType);
}
}
// 抽象策略類
abstract class AbstractStrategy implements Strategy {
// 類注冊方法
public void register() {
StrategyContext.registerStrategy(getClass().getSimpleName(), this);
}
}
// 單例外賣策略
class Waimai extends AbstractStrategy implements Strategy {
private static final Waimai instance = new Waimai();
private WaimaiService waimaiService;
private Waimai() {
register();
}
public static Waimai getInstance() {
return instance;
}
@Override
public void issue(Object... params) {
WaimaiRequest request = new WaimaiRequest();
// 構(gòu)建入?yún)?br> request.setWaimaiReq(params);
waimaiService.issueWaimai(request);
}
}
// 單例酒旅策略
class Hotel extends AbstractStrategy implements Strategy {
private static final Hotel instance = new Hotel();
private HotelService hotelService;
private Hotel() {
register();
}
public static Hotel getInstance() {
return instance;
}
@Override
public void issue(Object... params) {
HotelRequest request = new HotelRequest();
request.addHotelReq(params);
hotelService.sendPrize(request);
}
}
// 單例美食策略
class Food extends AbstractStrategy implements Strategy {
private static final Food instance = new Food();
private FoodService foodService;
private Food() {
register();
}
public static Food getInstance() {
return instance;
}
@Override
public void issue(Object... params) {
FoodRequest request = new FoodRequest(params);
foodService.payCoupon(request);
}
}
最終,小明設(shè)計完成的結(jié)構(gòu)類圖如下:
獎勵發(fā)放策略_類圖
如果使用了Spring框架,還可以利用Spring的Bean機制來代替上述的部分設(shè)計,直接使用@Component和@PostConstruct注解即可完成單例的創(chuàng)建和注冊,代碼會更加簡潔。至此,經(jīng)過了多次討論、反思和優(yōu)化,小明終于得到了一套低耦合高內(nèi)聚,同時符合開閉原則的設(shè)計?!袄蠋?,我開始學(xué)會利用設(shè)計模式去解決已發(fā)現(xiàn)的問題。這次我做得怎么樣?”“合格。但是,依然要戒驕戒躁?!?/p>
任務(wù)模型的設(shè)計
“之前讓你設(shè)計獎勵發(fā)放策略你還記得嗎?”老師忽然問道。“當(dāng)然記得。一個好的設(shè)計模式,能讓工作事半功倍?!毙∶鞔鸬??!班?,那會提到了活動營銷的組成部分,除了獎勵之外,貌似還有任務(wù)吧?!毙∶鼽c了點頭,老師接著說:“現(xiàn)在,我想讓你去完成任務(wù)模型的設(shè)計。你需要重點關(guān)注狀態(tài)的流轉(zhuǎn)變更,以及狀態(tài)變更后的消息通知?!毙∶餍廊唤酉铝死蠋熃o的難題。他首先定義了一套任務(wù)狀態(tài)的枚舉和行為的枚舉:
// 任務(wù)狀態(tài)枚舉
@AllArgsConstructor
@Getter
enum TaskState {
INIT("初始化"),
ONGOING( "進行中"),
PAUSED("暫停中"),
FINISHED("已完成"),
EXPIRED("已過期")
;
private final String message;
}
// 行為枚舉
@AllArgsConstructor
@Getter
enum ActionType {
START(1, "開始"),
STOP(2, "暫停"),
ACHIEVE(3, "完成"),
EXPIRE(4, "過期")
;
private final int code;
private final String message;
}
// 任務(wù)狀態(tài)枚舉
@AllArgsConstructor
@Getter
enum TaskState {
INIT("初始化"),
ONGOING( "進行中"),
PAUSED("暫停中"),
FINISHED("已完成"),
EXPIRED("已過期")
;
private final String message;
}
// 行為枚舉
@AllArgsConstructor
@Getter
enum ActionType {
START(1, "開始"),
STOP(2, "暫停"),
ACHIEVE(3, "完成"),
EXPIRE(4, "過期")
;
private final int code;
private final String message;
}
然后,小明對開始編寫狀態(tài)變更功能:
class Task {
private Long taskId;
// 任務(wù)的默認(rèn)狀態(tài)為初始化
private TaskState state = TaskState.INIT;
// 活動服務(wù)
private ActivityService activityService;
// 任務(wù)管理器
private TaskManager taskManager;
// 使用條件分支進行任務(wù)更新
public void updateState(ActionType actionType) {
if (state == TaskState.INIT) {
if (actionType == ActionType.START) {
state = TaskState.ONGOING;
}
} else if (state == TaskState.ONGOING) {
if (actionType == ActionType.ACHIEVE) {
state = TaskState.FINISHED;
// 任務(wù)完成后進對外部服務(wù)進行通知
activityService.notifyFinished(taskId);
taskManager.release(taskId);
} else if (actionType == ActionType.STOP) {
state = TaskState.PAUSED;
} else if (actionType == ActionType.EXPIRE) {
state = TaskState.EXPIRED;
}
} else if (state == TaskState.PAUSED) {
if (actionType == ActionType.START) {
state = TaskState.ONGOING;
} else if (actionType == ActionType.EXPIRE) {
state = TaskState.EXPIRED;
}
}
}
}
class Task {
private Long taskId;
// 任務(wù)的默認(rèn)狀態(tài)為初始化
private TaskState state = TaskState.INIT;
// 活動服務(wù)
private ActivityService activityService;
// 任務(wù)管理器
private TaskManager taskManager;
// 使用條件分支進行任務(wù)更新
public void updateState(ActionType actionType) {
if (state == TaskState.INIT) {
if (actionType == ActionType.START) {
state = TaskState.ONGOING;
}
} else if (state == TaskState.ONGOING) {
if (actionType == ActionType.ACHIEVE) {
state = TaskState.FINISHED;
// 任務(wù)完成后進對外部服務(wù)進行通知
activityService.notifyFinished(taskId);
taskManager.release(taskId);
} else if (actionType == ActionType.STOP) {
state = TaskState.PAUSED;
} else if (actionType == ActionType.EXPIRE) {
state = TaskState.EXPIRED;
}
} else if (state == TaskState.PAUSED) {
if (actionType == ActionType.START) {
state = TaskState.ONGOING;
} else if (actionType == ActionType.EXPIRE) {
state = TaskState.EXPIRED;
}
}
}
}
在上述的實現(xiàn)中,小明在updateState方法中完成了2個重要的功能:
- 接收不同的行為,然后更新當(dāng)前任務(wù)的狀態(tài);
- 當(dāng)任務(wù)過期時,通知任務(wù)所屬的活動和任務(wù)管理器。
誠然,隨著小明的系統(tǒng)開發(fā)能力和代碼質(zhì)量意識的提升,他能夠認(rèn)識到這種功能設(shè)計存在缺陷。“老師,我的代碼還是和之前說的那樣,不夠優(yōu)雅?!薄芭?,你自己說說看有什么問題?”“第一,方法中使用條件判斷來控制語句,但是當(dāng)條件復(fù)雜或者狀態(tài)太多時,條件判斷語句會過于臃腫,可讀性差,且不具備擴展性,維護難度也大。且增加新的狀態(tài)時要添加新的if-else語句,這違背了開閉原則,不利于程序的擴展。”老師表示同意,小明接著說:“第二,任務(wù)類不夠高內(nèi)聚,它在通知實現(xiàn)中感知了其他領(lǐng)域或模塊的模型,如活動和任務(wù)管理器,這樣代碼的耦合度太高,不利于擴展?!崩蠋熧澷p地說道:“很好,你有意識能夠自主發(fā)現(xiàn)代碼問題所在,已經(jīng)是很大的進步了?!薄澳沁@個問題應(yīng)該怎么去解決呢?”小明繼續(xù)發(fā)問?!斑@個同樣可以通過設(shè)計模式去優(yōu)化。首先是狀態(tài)流轉(zhuǎn)的控制可以使用狀態(tài)模式,其次,任務(wù)完成時的通知可以用到觀察者模式?!笔盏街甘竞?,小明馬上去學(xué)習(xí)了狀態(tài)模式的結(jié)構(gòu):
狀態(tài)模式:對有狀態(tài)的對象,把復(fù)雜的“判斷邏輯”提取到不同的狀態(tài)對象中,允許狀態(tài)對象在其內(nèi)部狀態(tài)發(fā)生改變時改變其行為。狀態(tài)模式包含以下主要角色:
- 環(huán)境類(Context)角色:也稱為上下文,它定義了客戶端需要的接口,內(nèi)部維護一個當(dāng)前狀態(tài),并負(fù)責(zé)具體狀態(tài)的切換。
- 抽象狀態(tài)(State)角色:定義一個接口,用以封裝環(huán)境對象中的特定狀態(tài)所對應(yīng)的行為,可以有一個或多個行為。
- 具體狀態(tài)(Concrete State)角色:實現(xiàn)抽象狀態(tài)所對應(yīng)的行為,并且在需要的情況下進行狀態(tài)切換。
根據(jù)狀態(tài)模式的定義,小明將TaskState枚舉類擴展成多個狀態(tài)類,并具備完成狀態(tài)的流轉(zhuǎn)的能力;然后優(yōu)化了任務(wù)類的實現(xiàn):
// 任務(wù)狀態(tài)抽象接口
interface State {
// 默認(rèn)實現(xiàn),不做任何處理
default void update(Task task, ActionType actionType) {
// do nothing
}
}
// 任務(wù)初始狀態(tài)
class TaskInit implements State {
@Override
public void update(Task task, ActionType actionType) {
if (actionType == ActionType.START) {
task.setState(new TaskOngoing());
}
}
}
// 任務(wù)進行狀態(tài)
class TaskOngoing implements State {
private ActivityService activityService;
private TaskManager taskManager;
@Override
public void update(Task task, ActionType actionType) {
if (actionType == ActionType.ACHIEVE) {
task.setState(new TaskFinished());
// 通知
activityService.notifyFinished(taskId);
taskManager.release(taskId);
} else if (actionType == ActionType.STOP) {
task.setState(new TaskPaused());
} else if (actionType == ActionType.EXPIRE) {
task.setState(new TaskExpired());
}
}
}
// 任務(wù)暫停狀態(tài)
class TaskPaus
本文名稱:設(shè)計模式二三事
文章路徑:http://www.dlmjj.cn/article/cophpje.html


咨詢
建站咨詢
