設(shè)計(jì)模式(適用場(chǎng)景 優(yōu)點(diǎn))
從Android代碼中來(lái)記憶23種設(shè)計(jì)模式
單例模式
確保單例類只有一個(gè)實(shí)例喜每,并且這個(gè)單例類提供一個(gè)函數(shù)接口讓其他類獲取到這個(gè)唯一的實(shí)例戏锹。
什么時(shí)候需要使用單例模式呢:如果某個(gè)類躯泰,創(chuàng)建時(shí)需要消耗很多資源毁枯,即new出這個(gè)類的代價(jià)很大;或者是這個(gè)類占用很多內(nèi)存,如果創(chuàng)建太多這個(gè)類實(shí)例會(huì)導(dǎo)致內(nèi)存占用太多。
//此為懶漢式 餓漢式在聲明時(shí)直接初始化
public class Singleton{
//volatile保證了其他線程拿到的也是最新的實(shí)例
private volatile static Singleton instance;
//將默認(rèn)的構(gòu)造函數(shù)私有化摩疑,防止其他類手動(dòng)new
private Singleton(){};
public static Singleton getInstance(){
if(instance==null){
synchronized(Singleton.class){
if(instance==null){
instance=new Singleton();
}
}
}
return instance;
}
}
為什么需要2次判斷是否為空呢危融?第一次判斷是為了避免不必要的同步畏铆,第二次判斷是確保在此之前沒(méi)有其他線程進(jìn)入到sychronized塊創(chuàng)建了新實(shí)例。
使用類級(jí)內(nèi)部類吉殃,只有內(nèi)部類被調(diào)用到(即Singleton.singleton)才會(huì)裝載
public class MySingleton {
/**
* 類級(jí)的內(nèi)部類辞居,也就是靜態(tài)的成員式內(nèi)部類,該內(nèi)部類的實(shí)例與外部類的實(shí)例
* 沒(méi)有綁定的關(guān)系蛋勺,而且只有被調(diào)用到才會(huì)裝載瓦灶,從而實(shí)現(xiàn)了延遲加載
*/
private static class Singleton{
/**
* 靜態(tài)初始化器,用JVM來(lái)保證線程安全
*/
private static MySingleton singleton = new MySingleton();
static {
System.out.println("---->類級(jí)的內(nèi)部類被加載");
}
private Singleton(){
System.out.println("---->類級(jí)的內(nèi)部類構(gòu)造函數(shù)被調(diào)用");
}
}
//私有化構(gòu)造函數(shù)
private MySingleton(){
System.out.println("-->開(kāi)始調(diào)用構(gòu)造函數(shù)");
}
public static MySingleton getInstance(){
System.out.println("-->開(kāi)始調(diào)用公有方法返回實(shí)例");
System.out.println("-->返回單例");
return Singleton.singleton;
}
}
記憶:getSystemService的內(nèi)部實(shí)現(xiàn)
建造者模式(Builder)
將一個(gè)復(fù)雜對(duì)象的構(gòu)造與它的表示分離抱完,使得同樣的構(gòu)造過(guò)程可以創(chuàng)建不同的表示贼陶。
主要是在創(chuàng)建某個(gè)對(duì)象時(shí),需要設(shè)定很多的參數(shù)(通過(guò)setter方法)巧娱,但是這些參數(shù)必須按照某個(gè)順序設(shè)定碉怔,或者是設(shè)置步驟不同會(huì)得到不同結(jié)果(模式不同后續(xù)的可用屬性也有不同)
記憶:在創(chuàng)建對(duì)話框時(shí)的代碼
身邊例子:通過(guò)方法構(gòu)建一個(gè)頁(yè)面,有無(wú)list,有無(wú)tab,有無(wú)column,每一個(gè)先導(dǎo)選擇都會(huì)導(dǎo)致后續(xù)使用參數(shù)的不同
工廠方法模式
定義一個(gè)創(chuàng)建對(duì)象的接口禁添,讓子類決定實(shí)例化哪個(gè)類
其實(shí)撮胧,在getSystemService方法中就是用到了工廠模式,他就是根據(jù)傳入的參數(shù)決定創(chuàng)建哪個(gè)對(duì)象老翘,當(dāng)然了芹啥,由于返回的都是以單例模式存在的對(duì)象,因此不用new了铺峭,直接把單例返回就好墓怀。
抽象工廠模式
為創(chuàng)建一組相關(guān)或者是相互依賴的對(duì)象提供一個(gè)接口,而不需要制定他們的具體類 (滿足依賴倒置原則)
public abstract class AbstractProductA{//標(biāo)題欄
public abstract void method();
}
public abstract class AbstractProdectB{//無(wú)數(shù)據(jù)
public abstract void method();
}
public abstract class AbstractFactory{//listTemplate
public abstract AbstractProductA createProductA();
public abstract AbstractProductB createProductB();
}
public class ConcreteFactory1 extends AbstractFactory{//listT實(shí)例1
public AbstractProductA createProductA(){
return new ConcreteProductA1();
}
public AbstractProductB createProductB(){
return new ConcreteProductB1();
}
}
public class ConcreteFactory2 extends AbstractFactory{//listT實(shí)例2
public AbstractProductA createProductA(){
return new ConcreteProductA2();
}
public AbstractProductB createProductB(){
return new ConcreteProductB2();
}
}
策略模式
定義:有一系列的算法卫键,將每個(gè)算法封裝起來(lái)(每個(gè)算法可以封裝到不同的類中)傀履,各個(gè)算法之間可以替換,策略模式讓算法獨(dú)立于使用它的客戶而獨(dú)立變化永罚。
其中在屬性動(dòng)畫(huà)中使用時(shí)間插值器的時(shí)候就用到了啤呼。在使用動(dòng)畫(huà)時(shí)卧秘,你可以選擇線性插值器、加速減速插值器官扣、減速插值器以及自定義的插值器翅敌。這些插值器都是實(shí)現(xiàn)根據(jù)時(shí)間流逝的百分比來(lái)計(jì)算出當(dāng)前屬性值改變的百分比。通過(guò)根據(jù)需要選擇不同的插值器惕蹄,實(shí)現(xiàn)不同的動(dòng)畫(huà)效果蚯涮。
使用者可能會(huì)覺(jué)得有工廠方法的影子,因?yàn)樯衔闹刑岬降?strong>線性插值器卖陵、加速減速插值器遭顶、減速插值器以及自定義的插值器,很明顯可以通過(guò)工廠方法構(gòu)建出來(lái)泪蔫,但是策略模式是代表了將每個(gè)算法封裝起來(lái)(每個(gè)算法可以封裝到不同的類中)棒旗,各個(gè)算法之間可以替換,策略模式讓算法獨(dú)立于使用它的客戶而獨(dú)立變化撩荣,側(cè)重點(diǎn)不同铣揉。所以我們要知道,同時(shí)使用多種設(shè)計(jì)模式是很常見(jiàn)的餐曹。
身邊的例子:生成申請(qǐng)類的工廠方法逛拱,只需要一個(gè)變量的改變,如果申請(qǐng)類內(nèi)部存在算法台猴,則應(yīng)用了策略模式朽合。可以說(shuō)工廠方法可以作為策略模式的實(shí)現(xiàn)方式饱狂,但他不是唯一的
反例:使用了建造者模式的工廠方法
適配器模式
定義:把一個(gè)類的接口變換成客戶端所期待的另一個(gè)接口曹步,從而使原本因接口不匹配而無(wú)法在一起工作的兩個(gè)類能夠在一起工作。
比較典型的有ListView和RecyclerView的adapter嗡官。為什么ListView需要使用適配器呢箭窜?主要是,ListView(view)只關(guān)心它的每個(gè)ItemView衍腥,而不關(guān)心這個(gè)ItemView具體顯示的是什么磺樱。而我們的數(shù)據(jù)源(model)存放的是要顯示的內(nèi)容,它保存了每一個(gè)ItemView要顯示的內(nèi)容婆咸。ListView和數(shù)據(jù)源之間沒(méi)有任何關(guān)系竹捉,這時(shí)候,需要通過(guò)適配器(adapter)尚骄,適配器提供getView方法給ListView使用块差,每次ListView只需提供位置信息給getView函數(shù),然后getView函數(shù)根據(jù)位置信息向數(shù)據(jù)源獲取對(duì)應(yīng)的數(shù)據(jù),根據(jù)數(shù)據(jù)返回不同的View憨闰。
觀察者模式
定義:定義了對(duì)象之間的一對(duì)多的關(guān)系状蜗,其實(shí)就是1對(duì)n,當(dāng)“1”發(fā)生變化時(shí)鹉动,“n”全部得到通知轧坎,并更新。
觀察者模式一個(gè)比較經(jīng)典的應(yīng)用就是:訂閱——發(fā)布系統(tǒng)泽示。很容易理解缸血,發(fā)布消息時(shí),將消息發(fā)送給每個(gè)訂閱者械筛。
那么Android哪里用到了觀察者模式呢捎泻?我們看看ListView的適配器,有個(gè)函數(shù)notifyDataSetChanged()函數(shù)埋哟,這個(gè)函數(shù)其實(shí)就是通知ListView的每個(gè)Item笆豁,數(shù)據(jù)源發(fā)生了變化,請(qǐng)各位Item重新刷新一下定欧。
//抽象主題
public interface MySubject {
void registerObserver(MyObserver o);
void removeObserver(MyObserver o);
void notifyObserver();
}
//抽象觀察者
public interface MyObserver {
void update(String authorName, String articleName);
}
//實(shí)現(xiàn)主題
public class WeChatServer implements MySubject {
private List<MyObserver> myObservers;
private String authorName;
private String articleName;
public WeChatServer(String authorName) {
myObservers = new ArrayList<>();
this.authorName = authorName;
}
public void publishArticle(String articleName) {
this.articleName = articleName;
notifyObserver();
}
@Override
public void registerObserver(MyObserver o) {
myObservers.add(o);
}
@Override
public void removeObserver(MyObserver o) {
if (myObservers.contains(o)) {
myObservers.remove(o);
}
}
@Override
public void notifyObserver() {
myObservers.forEach(item -> {
item.update(authorName, articleName);
});
}
}
//具體的觀察者
public class WeChatClient implements MyObserver {
private String username;
public WeChatClient(String username) {
this.username = username;
}
@Override
public void update(String authorName, String articleName) {
System.out.println(username + ": " + authorName + " 發(fā)了一篇文章 " + articleName);
}
}
public class Main {
public static void main(String[] args) {
WeChatServer weChatServer = new WeChatServer("Java識(shí)堂");
WeChatClient user1 = new WeChatClient("張三");
WeChatClient user2 = new WeChatClient("李四");
weChatServer.registerObserver(user1);
weChatServer.registerObserver(user2);
weChatServer.publishArticle("《五分鐘學(xué)會(huì)觀察者模式》");
}
}
裝飾模式
動(dòng)態(tài)的給一個(gè)對(duì)象添加額外的職責(zé)渔呵,就增加功能來(lái)說(shuō)怒竿,裝飾模式比子類繼承的方式更靈活砍鸠。
通過(guò)簡(jiǎn)單代碼來(lái)理解裝飾模式:
public abstract class Component{
public abstract void operate();
}
public class ConcreteComponent extends Component{
public void operate(){
//具體的實(shí)現(xiàn)
}
}
public class Decorator{
private Component component;
public Decorator(Component component){
this.component=component;
}
public void operate(){
operateA();
component.operate();
operateB();
}
public void operateA(){
//具體操作
}
public void operateB(){
//具體操作
}
}
那么在Android哪里出現(xiàn)了裝飾模式呢?我們平時(shí)經(jīng)常用到Context類耕驰,但是其實(shí)Context類只是個(gè)抽象類爷辱,具體實(shí)現(xiàn)是ContextImpl,那么誰(shuí)是ContextImpl的裝飾類呢朦肘?我們知道Activity是個(gè)Context,但是Activity 并不是繼承于Context,而是繼承于ContextThremeWrapper.而ContextThremeWrapper繼承于ContextWrapper,ContextWrapper繼承Context.說(shuō)了這么多饭弓,跟裝飾模式有啥關(guān)系?主要是引入ContextWrapper這個(gè)類媒抠。ContextWrapper內(nèi)部有個(gè)Context引用mContext弟断,并且ContextWrapper中對(duì)Context的每個(gè)方法都有實(shí)現(xiàn),在實(shí)現(xiàn)中調(diào)用的就是mContext相同的方法趴生。
身邊的例子:BaseActivity+MyBaseActivity(通過(guò)抽象類+實(shí)現(xiàn)類的組合實(shí)現(xiàn)職責(zé)添加)