設計模式
- 所謂設計模式,就是一套被反復使用的代碼設計經驗的總結(情境中一個問題經過證實的一個解決方案)。使用設計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。設計模式使人們可以更加簡單方便的復用成功的設計和體系結構。將已證實的技術表述成設計模式也會使新系統開發者更加容易理解其設計思路。
- 在GoF的《Design Patterns: Elements of Reusable Object-Oriented Software》中給出了三類(創建型[對類的實例化過程的抽象化]、結構型[描述如何將類或對象結合在一起形成更大的結構]、行為型[對在不同的對象之間划分責任和算法的抽象化])共23種設計模式,包括:Abstract Factory(抽象工廠模式),Builder(建造者模式),Factory Method(工廠方法模式),Prototype(原始模型模式),Singleton(單例模式);Facade(門面模式),Adapter(適配器模式),Bridge(橋梁模式),Composite(合成模式),Decorator(裝飾模式),Flyweight(享元模式),Proxy(代理模式);Command(命令模式),Interpreter(解釋器模式),Visitor(訪問者模式),Iterator(迭代子模式),Mediator(調停者模式),Memento(備忘錄模式),Observer(觀察者模式),State(狀態模式),Strategy(策略模式),Template Method(模板方法模式), Chain Of Responsibility(責任鏈模式)。
哪些源碼用到了設計模式
責任鏈模式:try-catch、有序廣播、viewgroup事件傳遞
建造者模式:AlertDialog
裝飾模式:Collections工具類、I/O、context
觀察者模:android中的回調模式、listview的notifyDataChanged、rxjava
外觀模式:context
模板方法模式:AsnycTask、Activity
策略:Volley
單例
保證一個類在內存中的對象唯一性。
1,不允許其他程序用new創建該類對象。
2,在該類創建一個本類實例。
3,對外提供一個方法讓其他程序可以獲取該對象。
好處:
對於頻繁使用的對象,可以省略創建對象所花費的時間,這對於那些重量級對象而言,是非常可觀的一筆系統開銷
由於new操作的次數減少,因而對系統內存的使用頻率也會降低,這將減輕GC壓力,縮短GC停頓時間
餓漢
public class HungurySingleton {
private static final HungurySingleton mHungurySingleton = new HungurySingleton();
private HungurySingleton(){
System.out.println("Singleton is create");
}
public static HungurySingleton getHungurySingleton() {
return mHungurySingleton;
}
public static void createString(){
System.out.println("createString in Singleton");
}
public static void main(String[] args){
HungurySingleton.createString();
}
}
懶漢
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {
}
public static LazySingleton getInstance() {
// 第一次調用的時候會被初始化
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
public static void createString(){
System.out.println("create String");
}
public static void main(String[] args){
LazySingleton.createString();
}
}
懶漢安全
public class LazySafetySingleton {
private static LazySafetySingleton instance;
private LazySafetySingleton (){
}
public static void createString(){
System.out.println("create String");
}
public static void main(String[] args){
LazySingleton.createString();
}
//方法中聲明synchronized關鍵字
public static synchronized LazySafetySingleton getInstance() {
if (instance == null) {
instance = new LazySafetySingleton();
}
return instance;
}
//同步代碼塊實現
public static LazySafetySingleton getInstance1() {
synchronized (LazySafetySingleton.class) {
if(instance == null){//懶漢式
instance = new LazySafetySingleton();
}
}
return instance;
}
}
DCL
假設沒有關鍵字volatile的情況下,兩個線程A、B,都是第一次調用該單例方法,線程A先執行instance = new Instance(),該構造方法是一個非原子操作,編譯后生成多條字節碼指令,由於JAVA的指令重排序,可能會先執行instance的賦值操作,該操作實際只是在內存中開辟一片存儲對象的區域后直接返回內存的引用,之后instance便不為空了,但是實際的初始化操作卻還沒有執行,如果就在此時線程B進入,就會看到一個不為空的但是不完整(沒有完成初始化)的Instance對象,所以需要加入volatile關鍵字,禁止指令重排序優化,從而安全的實現單例。
加入同步為了解決多線程安全問題。
加入雙重判斷是為了解決效率問題。
不寫if語句的話,每次都會判斷同步鎖,這樣寫的話只在第一次判斷並創建對象。以后在不會執行if里的代碼了
public class DclSingleton {
private static volatile DclSingleton mInstance = null;
// private static DclSingleton mInstance = null;
private DclSingleton() {
}
public void doSomething() {
System.out.println("do sth.");
}
public static DclSingleton getInstance() {
// 避免不必要的同步
if (mInstance == null) {
// 同步
synchronized (DclSingleton.class) {
// 在第一次調用時初始化
if (mInstance == null) {
mInstance = new DclSingleton();
}
}
}
return mInstance;
}
}
靜態內部類單例(推薦)
靜態內部類的優點是:外部類加載時並不需要立即加載內部類,內部類不被加載則不去初始化INSTANCE,故而不占內存。
即當SingleTon第一次被加載時,並不需要去加載SingleTonHoler,只有當getInstance()方法第一次被調用時,才會去初始化INSTANCE,第一次調用getInstance()方法會導致虛擬機加載SingleTonHoler類,這種方法不僅能確保線程安全,也能保證單例的唯一性,同時也延遲了單例的實例化。
public class SingleTon{
private SingleTon(){}
private static class SingleTonHoler{
private static SingleTon INSTANCE = new SingleTon();
}
public static SingleTon getInstance(){
return SingleTonHoler.INSTANCE;
}
枚舉單例(推薦)
public enum EnumSingleton {
//定義一個枚舉的元素,它就是 Singleton 的一個實例
INSTANCE;
public void doSomeThing() {
// do something...
}
}
總結
餓漢:無法對instance實例進行延遲加載
懶漢:多線程並發情況下無法保證實例的唯一性
懶漢線程安全:使用synchronized導致性能缺陷
DCL:JVM即使編譯器的指令重排序
靜態內部類/枚舉:延遲加載/線程安全/性能優勢
外觀模式
外觀模式的主要目的在於讓外部減少與子系統內部多個模塊的交互,從而讓外部能夠更簡單得使用子系統。它負責把客戶端的請求轉發給子系統內部的各個模塊進行處理。
使用場景
1)當你要為一個復雜子系統提供一個簡單接口時。
2)客戶程序與抽象類的實現部分之間存在着很大的依賴性
3)當你需要構建一個層次結構的子系統時
優點:
1)由於Facade類封裝了各個模塊交互的過程,如果今后內部模塊調用關系發生了變化,只需要修改Facade實現就可以了。
2) Facade實現是可以被多個客戶端調用的
public class ModuleA {
public void testFuncA() {
System.out.println("This is Function From 改變了");
}
}
public class ModuleB {
public void testFuncB() {
System.out.println("This is Function From ModuleB");
}
}
public class Facade {
private ModuleA moduleA = null;
private ModuleB moduleB = null;
private ModuleC moduleC = null;
private static Facade mFacade = null;
private Facade(){
moduleA = new ModuleA();
moduleB = new ModuleB();
moduleC = new ModuleC();
}
public static Facade getInstance() {
if(mFacade == null) {
mFacade = new Facade();
}
return mFacade;
}
public void testOperation() {
moduleA.testFuncA();
moduleB.testFuncB();
moduleC.testFuncC();
}
}
public class Client {
public static void main(String arg[]) {
Facade.getInstance().testOperation();
}
}
裝飾模式
- 對一組對象的功能進行增強時,就可以使用該模式進行問題的解決,是一種對象結構型模式。裝飾和繼承都能實現一樣的特點:進行功能的擴展增強。 但是只為提高功能,進行的繼承,會導致繼承體系越來越臃腫,不夠靈活。
- 與繼承關系相比,關聯關系的主要優勢在於不會破壞類的封裝性,而且繼承是一種耦合度較大的靜態關系,無法在程序運行時動態擴展。在軟件開發階段,關聯關系雖然不會比繼承關系減少編碼量,但是到了軟件維護階段,由於關聯關系使系統耦合性不高,因此使得系統更加容易維護。當然,關聯關系的缺點是比繼承關系要創建更多的對象。
使用場景
(1)在不影響其他對象的情況下,以動態、透明的方式給單個對象添加職責。
(2)當不能采用繼承的方式對系統進行擴展或者采用繼承不利於系統擴展和維護時可以使用裝飾模式
- Component: 抽象構件
- ConcreteComponent: 具體構件
- Decorator: 抽象裝飾類
- ConcreteDecorator: 具體裝飾類
ConcreteDecorator和Decorator都是繼承component
public class DecoratorExample {
/**
* 抽象構件
*/
interface Component {
void operation();
}
/**
* 具體構件
*/
class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("from ConcreteComponent");
}
}
/**
* 抽象裝飾類
*/
class Decorator implements Component {
private Component component; //維持一個對抽象構件對象的引用
public Decorator(Component component) { //注入一個抽象構件類型的對象
this.component = component;
}
@Override
public void operation() {
component.operation(); //調用原有業務方法
}
}
/**
* 具體裝飾類
*/
class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operation() {
//倆方法位置隨便替換
super.operation(); //調用原有業務方法
addedBehavior(); //調用新增業務方法
}
//新增業務方法
public void addedBehavior() {
System.out.println("from ConcreteDecoratorA");
}
}
class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedBehavior();
}
//新增業務方法
public void addedBehavior() {
System.out.println("from ConcreteDecoratorB");
}
}
public static void main(String[] args) {
//為了方便看就寫到一個類里了,理解意思即可
// ConcreteComponent concreteComponent = new ConcreteComponent();
// Decorator decorator = new Decorator(concreteComponent);
// ConcreteDecoratorA concreteDecoratorA = new ConcreteDecoratorA(decorator);
// ConcreteDecoratorB concreteDecoratorB = new ConcreteDecoratorB(concreteDecoratorA);
// concreteDecoratorB.operation();
}
}
工廠模式
工廠類可以根據條件生成不同的子類實例,這些子類有一個公共的抽象父類並且實現了相同的方法,但是這些方法針對不同的數據進行了不同的操作(多態方法)。當得到子類的實例后,開發人員可以調用基類中的方法而不必考慮到底返回的是哪一個子類的實例。
工廠類可以根據條件生成不同的子類實例,並且這些對象需要具有共同的接口。
工廠方法模式(Factory Method)分為3種:
1、普通工廠模式
就是建立一個工廠類,對實現了同一接口的一些類進行實例的創建。首先看下關系圖:


我們以一個例子來講解:發送短信和發送郵件(具有共同的接口:發送)

public interface Sender {
/*
* 發送郵件或者短消息的共同接口
*/
public void sender();
}
/*
* 發送郵件的實現類
*/
public class MailSender implements Sender {
public void sender() {
// TODO Auto-generated method stub
System.out.println("this is mailsender!");
}
}
/*
* 發送短信的實現類
*/
public class SMSSender implements Sender {
public void sender() {
// TODO Auto-generated method stub
System.out.println("this is sms sender!");
}
}
public class SendFactory {
public Sender produce(String type){
if ("mial".equals(type)) {
//根據類型生產對象
return new MailSender();
}else if("sms".equals(type)) {
//根據類型生產對象
return new SMSSender();
}else {
System.out.println("類型輸入錯誤");
return null;
}
}
}
工廠方法模式:這是對普通工廠方法模式的改進,在普通工廠方法模式中,如果傳遞的字符串出錯,則不能正確創建對象,而多個工廠方法模式是提供多個工廠方法,分別創建對象。關系圖:

public class SendFactory {
public Sender produceMail(){
return new MailSender();
}
public Sender produceSms(){
return new SmsSender();
}
}
public class FactoryTest {
public static void main(String[] args) {
SendFactory factory = new SendFactory();
Sender sender = factory.produceMail();
sender.Send();
}
}
靜態工廠模式:將上面的多個工廠方法模式里的方法置為靜態的,不需要創建實例,直接調用即可
public class SendFactory {
public static Sender produceMail(){
return new MailSender();
}
public static Sender produceSms(){
return new SmsSender();
}
}
public class FactoryTest {
public static void main(String[] args) {
Sender sender = SendFactory.produceMail();
sender.Send();
}
}
工廠模式適用於:凡是出現了大量的產品需要創建,並且具有共同的接口時,可以通過工廠方法模式進行創建。在以上的三種模式中,第一種如果傳入的字符串有誤,不能正確創建對象,第三種相對於第二種,不需要實例化工廠類,所以,大多數情況下,我們會選用第三種——靜態工廠方法模式。
抽象工廠模式(Abstract Factory)
工廠方法模式有一個問題就是,類的創建依賴工廠類,也就是說,如果想要拓展程序,必須對工廠類進行修改,這違背了閉包原則,所以,從設計角度考慮,有一定的問題,如何解決?就用到抽象工廠模式,創建多個工廠類,這樣一旦需要增加新的功能,直接增加新的工廠類就可以了,不需要修改之前的代碼。因為抽象工廠不太好理解,我們先看看圖,然后就和代碼,就比較容易理解。

這個模式的好處就是,如果你現在想增加一個功能:發及時信息,則只需做一個實現類,實現Sender接口,同時做一個工廠類,實現Provider接口,就OK了,無需去改動現成的代碼。這樣做,拓展性較好!

public interface Sender {
public void Send();
}
public class MailSender implements Sender {
@Override
public void Send() {
System.out.println("this is mailsender!");
}
}
public class SmsSender implements Sender {
@Override
public void Send() {
System.out.println("this is sms sender!");
}
}
//工廠類
public class SendMailFactory implements Provider {
@Override
public Sender produce(){
return new MailSender();
}
}
public class SendSmsFactory implements Provider{
@Override
public Sender produce() {
return new SmsSender();
}
}
public interface Provider {
public Sender produce();
}
public class Test {
public static void main(String[] args) {
Provider provider = new SendMailFactory();
Sender sender = provider.produce();
sender.Send();
}
}
相關源碼:
https://github.com/peiniwan/DesignPattern.git
