小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

記錄自己理解的一些設(shè)計(jì)模式

 釋皇天 2017-04-23


來(lái)源:fangjian0423,

fangjian0423.github.io/2017/03/26/design-pattern/

如有好文章投稿,請(qǐng)點(diǎn)擊 → 這里了解詳情


記錄一下自己理解的一些設(shè)計(jì)模式,并盡量使用表達(dá)清楚的例子進(jìn)行講解。


策略模式


策略模式應(yīng)該是最基礎(chǔ)的一個(gè)設(shè)計(jì)模式,它是對(duì)行為的一個(gè)抽象。jdk中的Comparator比較器就是一個(gè)使用策略設(shè)計(jì)模式的策略。


比如有一個(gè)Student學(xué)生類,有name和age兩個(gè)屬性。如果有個(gè)需求需要打印學(xué)生名單,并按照字母順序排序,可以使用Comparator接口并在內(nèi)部使用name進(jìn)行比較即可。 如果哪一天需要按照年齡進(jìn)行排序,那么只需要修改Comparator即可,也就是使用一個(gè)新的策略,其它完全不變。


工廠模式


工廠模式的意義在于對(duì)象的創(chuàng)建、管理可以使用工廠去管理,而不是創(chuàng)建者自身。最典型的工廠模式使用者就是Spring,Spring內(nèi)部的容器就是一個(gè)工廠,所有的bean都由這個(gè)容器管理,包括它們的創(chuàng)建、銷毀、注入都被這個(gè)容器管理。


工廠模式分簡(jiǎn)單工廠和抽象工廠。它們的區(qū)別在于抽象工廠抽象程度更高,把工廠也抽象成了一個(gè)接口,這樣可以再每添加一個(gè)新的對(duì)象的時(shí)候而不需要修改工廠的代碼。


比如有個(gè)Repository接口,用于存儲(chǔ)數(shù)據(jù),有DatabaseRepository,CacheRepository,F(xiàn)ileRepository分別在數(shù)據(jù)庫(kù),緩存,文件中存儲(chǔ)數(shù)據(jù),定義如下:


public interface Repository {

    void save(Object obj);

}

 

class DatabaseRepository implements Repository {

    @Override

    public void save(Object obj) {

        System.out.println('save in database');

    }

}

class CacheRepository implements Repository {

    @Override

    public void save(Object obj) {

        System.out.println('save in cache');

    }

}

class FileRepository implements Repository {

    @Override

    public void save(Object obj) {

        System.out.println('save in file');

    }

}


簡(jiǎn)單工廠的使用


public class RepositoryFactory {

 

    public Repository create(String type) {

        Repository repository = null;

        switch (type) {

            case 'db':

                repository = new DatabaseRepository();

                break;

            case 'cache':

                repository = new CacheRepository();

                break;

            case 'file':

                repository = new FileRepository();

                break;

        }

        return repository;

    }

 

    public static void main(String[] args) {

        RepositoryFactory factory = new RepositoryFactory();

        factory.create('db').save(new Object());

        factory.create('cache').save(new Object());

        factory.create('file').save(new Object());

    }

}


簡(jiǎn)單工廠的弊端在于每添加一個(gè)新的Repository,都必須修改RepositoryFactory中的代碼。


抽象工廠的使用


public interface RepositoryFactoryProvider {

    Repository create();

}

 

class DatabaseRepositoryFactory implements RepositoryFactoryProvider {

    @Override

    public Repository create() {

        return new DatabaseRepository();

    }

}

class CacheRepositoryFactory implements RepositoryFactoryProvider {

    @Override

    public Repository create() {

        return new CacheRepository();

    }

}

class FileRepositoryFactory implements RepositoryFactoryProvider {

    @Override

    public Repository create() {

        return new FileRepository();

    }

}


抽象工廠的測(cè)試:


RepositoryFactoryProvider dbProvider = new DatabaseRepositoryFactory();

dbProvider.create().save(new Object());

RepositoryFactoryProvider cacheProvider = new CacheRepositoryFactory();

cacheProvider.create().save(new Object());

RepositoryFactoryProvider fileProvider = new FileRepositoryFactory();

fileProvider.create().save(new Object());


抽象工廠把工廠也進(jìn)行了抽象話,所以添加一個(gè)新的Repository的話,只需要新增一個(gè)RepositoryFactory即可,原有代碼不需要修改。


裝飾者模式


裝飾者模式的作用就在于它可以在不改變?cè)蓄惖幕A(chǔ)上動(dòng)態(tài)地給類添加新的功能。之前寫過(guò)一篇通過(guò)源碼分析MyBatis的緩存文章,mybatis中的query就是使用了裝飾者設(shè)計(jì)模式。


用一段簡(jiǎn)單的代碼來(lái)模擬一下mybatis中query的實(shí)現(xiàn)原理:


@Data

@AllArgsConstructor

@ToString

class Result { // 查詢結(jié)果類,相當(dāng)于一個(gè)domain

  private Object obj;

  private String sql;

}

 

public interface Query { // 查詢接口,有簡(jiǎn)單查詢和緩存查詢

  Result query(String sql);

}

 

public class SimpleQuery implements Query { // 簡(jiǎn)單查詢,相當(dāng)于直接查詢數(shù)據(jù)庫(kù),這里直接返回Result,相當(dāng)于是數(shù)據(jù)庫(kù)查詢的結(jié)果

  @Override

  public Result query(String sql) {

      return new Result(new Object(), sql);

  }

}

 

public class CacheQuery implements Query { // 緩存查詢,如果查詢相同的sql,不直接查詢數(shù)據(jù)庫(kù),而是返回map中存在的Result

  private Query query;

  private Map cache = new HashMap<>();

  public CacheQuery(Query query) {

      this.query = query;

  }

  @Override

  public Result query(String sql) {

      if(cache.containsKey(sql)) {

          return cache.get(sql);

      }

      Result result = query.query(sql);

      cache.put(sql, result);

      return result;

  }

}


測(cè)試:


Query simpleQuery = new SimpleQuery();

System.out.println(simpleQuery.query('select * from t_student') == simpleQuery.query('select * from t_student')); // false

Query cacheQuery = new CacheQuery(simpleQuery);

System.out.println(cacheQuery.query('select * from t_student') == cacheQuery.query('select * from t_student')); // true


這里CacheQuery就是一個(gè)裝飾類,SimpleQuery是一個(gè)被裝飾者。我們通過(guò)裝飾者設(shè)計(jì)模式動(dòng)態(tài)地給SimpleQuery添加了緩存功能,而不需要修改SimpleQuery的代碼。


當(dāng)然,裝飾者模式也有缺點(diǎn),就是會(huì)存在太多的類。


如果我們需要添加一個(gè)過(guò)濾的查詢(sql中有敏感字的就直接返回null,而不查詢數(shù)據(jù)庫(kù)),只需要可以添加一個(gè)FilterQuery裝飾者即可:


public class FilterQuery implements Query {

    private Query query;

    private List words = new ArrayList<>();

    public FilterQuery(Query query) {

        this.query = query;

        words.add('fuck');

        words.add('sex');

    }

    @Override

    public Result query(String sql) {

        for(String word : words) {

            if(sql.contains(word)) return null;

        }

        return query.query(sql);

    }

}

 

Query filterQuery = new FilterQuery(simpleQuery);

System.out.println(filterQuery.query('select * from t_student where name = 'fuck''));  // null

System.out.println(filterQuery.query('select * from t_student where name = 'format'')); // Result(obj=java.lang.Object@1b4fb997, sql=select * from t_student where name = 'format')


代理模式


代理模式的作用是使用一個(gè)代理類來(lái)代替原先類進(jìn)行操作。比較常見的就是aop中就是使用代理模式完成事務(wù)的處理。


代理模式分靜態(tài)代理和動(dòng)態(tài)代理,靜態(tài)代理的原理就是對(duì)目標(biāo)對(duì)象進(jìn)行封裝,最后調(diào)用目標(biāo)對(duì)象的方法即可。


動(dòng)態(tài)代理跟靜態(tài)代理的區(qū)別就是動(dòng)態(tài)代理中的代理類是程序運(yùn)行的時(shí)候生成的。Spring中對(duì)于接口的代理使用jdk內(nèi)置的Proxy和InvocationHandler實(shí)現(xiàn),對(duì)于類的代理使用cglib完成。


以1個(gè)UserService為例,使用jdk自帶的代理模式完成計(jì)算方法調(diào)用時(shí)間的需求:


// UserService接口

public interface IUserService {

    void printAll();

}

// UserService實(shí)現(xiàn)類

class UserService implements IUserService {

    @Override

    public void printAll() {

        System.out.println('print all users');

    }

}

// InvocationHandler策略,這里打印了方法調(diào)用前后的時(shí)間

@AllArgsConstructor

class UserInvocationHandler implements InvocationHandler {

    private IUserService userService;

    @Override

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println('start : ' + System.currentTimeMillis());

        Object result = method.invoke(userService, args);

        System.out.println('end : ' + System.currentTimeMillis());

        return result;

    }

}


測(cè)試:


IUserService userService = new UserService();

UserInvocationHandler uih = new UserInvocationHandler(userService);

IUserService proxy = (IUserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), new Class[] {IUserService.class}, uih);

proxy.printAll(); // 打印出start : 1489665566456  print all users  end : 1489665566457


組合模式


組合模式經(jīng)常跟策略模式配合使用,用來(lái)組合所有的策略,并遍歷這些策略找出滿足條件的策略。之前寫過(guò)一篇SpringMVC關(guān)于json、xml自動(dòng)轉(zhuǎn)換的原理研究文章,里面springmvc把返回的返回值映射給用戶的response做了一層抽象,封裝到了HandlerMethodReturnValueHandler策略接口中。


在HandlerMethodReturnValueHandlerComposite類中,使用存在的HandlerMethodReturnValueHandler對(duì)返回值進(jìn)行處理,在HandlerMethodReturnValueHandlerComposite內(nèi)部的代碼如下:


// 策略集合

private final List returnValueHandlers = new ArrayList();

 

@Override

public void handleReturnValue(Object returnValue, MethodParameter returnType,

    ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

    // 調(diào)用selectHandler方法

    HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);

    if (handler == null) {

      throw new IllegalArgumentException('Unknown return value type: ' + returnType.getParameterType().getName());

    }

    handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); // 使用找到的handler進(jìn)行處理

}

 

private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) {

    boolean isAsyncValue = isAsyncReturnValue(value, returnType);

    // 遍歷存在的HandlerMethodReturnValueHandler

    for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {

      if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {

        continue;

      }

      if (handler.supportsReturnType(returnType)) { // 找到匹配的handler

        return handler;

      }

    }

    return null;

}


模板模式


跟策略模式類似,模板模式會(huì)先定義好實(shí)現(xiàn)的邏輯步驟,但是具體的實(shí)現(xiàn)方式由子類完成,跟策略模式的區(qū)別就是模板模式是有邏輯步驟的。比如要給院系里的學(xué)生排序,并取出排名第一的學(xué)生。這里就有2個(gè)步驟,分別是排序和取出第一名學(xué)生。


一段偽代碼:


public abstract class AbstractStudentGetter {

    public final Student getStudent(List students) {

        sort(students); // 第一步

        if(!CollectionUtils.isEmpty(students)) {

            return students.get(0);  // 第二步

        }

        return null;

    }

    abstract public void sort(List students);

}

class AgeStudentGetter extends AbstractStudentGetter { // 取出年紀(jì)最大的學(xué)生

    @Override

    public void sort(List students) {

        students.sort(new Comparator() {

            @Override

            public int compare(Student s1, Student s2) {

                return s2.getAge() - s1.getAge();

            }

        });

    }

}

class NameStudentGetter extends AbstractStudentGetter { // 按照名字字母排序取出第一個(gè)學(xué)生

    @Override

    public void sort(List students) {

        students.sort(new Comparator() {

            @Override

            public int compare(Student s1, Student s2) {

                return s2.getName().compareTo(s1.getName());

            }

        });

    }

}


測(cè)試:


AbstractStudentGetter ageGetter = new AgeStudentGetter();

AbstractStudentGetter nameGetter = new NameStudentGetter();

 

List students = new ArrayList<>();

students.add(new Student('jim', 22));

students.add(new Student('format', 25));

 

System.out.println(ageGetter.getStudent(students)); // Student(name=format, age=25)

System.out.println(nameGetter.getStudent(students)); // Student(name=jim, age=22)


觀察者設(shè)計(jì)模式


觀察者設(shè)計(jì)模式主要的使用場(chǎng)景在于一個(gè)對(duì)象變化之后,依賴該對(duì)象的對(duì)象會(huì)收到通知。典型的例子就是rss的訂閱,當(dāng)訂閱了博客的rss之后,當(dāng)博客更新之后,訂閱者就會(huì)收到新的訂閱信息。


jdk內(nèi)置提供了Observable和Observer,用來(lái)實(shí)現(xiàn)觀察者模式:


// 定義一個(gè)Observable

public class MetricsObserable extends Observable {

    private Map counterMap = new HashMap<>();

    public void updateCounter(String key, Long value) {

        counterMap.put(key, value);

        setChanged();

        notifyObservers(counterMap);

    }

}

// Observer

public class AdminA implements Observer {

    @Override

    public void update(Observable o, Object arg) {

        System.out.println('adminA: ' + arg);

    }

}

public class AdminB implements Observer {

    @Override

    public void update(Observable o, Object arg) {

        System.out.println('adminB: ' + arg);

    }

}


測(cè)試:


MetricsObserable metricsObserable = new MetricsObserable();

metricsObserable.addObserver(new AdminA());

metricsObserable.addObserver(new AdminB());

metricsObserable.updateCounter('request-count', 100l);


打印出:


adminB: {request-count=100}

adminA: {request-count=100}


享元模式


線程池中會(huì)構(gòu)造幾個(gè)核心線程用于處理,這些線程會(huì)去取阻塞隊(duì)列里的任務(wù)然后進(jìn)行執(zhí)行。這些線程就是會(huì)被共享、且被重復(fù)使用的。因?yàn)榫€程的創(chuàng)建、銷毀、調(diào)度都是需要消耗資源的,沒(méi)有必要每次創(chuàng)建新的線程,而是共用一些線程。這就是享元模式的使用。類似的還有jdbc連接池,對(duì)象池等。


之前有一次面試被問(wèn)到:


Integer.valueOf('1') == Integer.valueOf('1') // true還是false


當(dāng)時(shí)回答的是false,后來(lái)翻了下Integer的源碼發(fā)現(xiàn)Integer里面有個(gè)內(nèi)部類IntegerCache,用于緩存一些共用的Integer。這個(gè)緩存的范圍可以在jvm啟動(dòng)的時(shí)候進(jìn)行設(shè)置。


其實(shí)后來(lái)想想也應(yīng)該這么做,我們沒(méi)有必要每次使用對(duì)象的時(shí)候都返回新的對(duì)象,可以共享這些對(duì)象,因?yàn)樾聦?duì)象的創(chuàng)建都是需要消耗內(nèi)存的。


適配器模式


適配器模式比較好理解。像生活中插線口的插頭有2個(gè)口的,也有3個(gè)口的。如果電腦的電源插口只有3個(gè)口的,但是我們需要一個(gè)2個(gè)口的插口的話,這個(gè)時(shí)候就需要使用插座來(lái)外接這個(gè)3個(gè)口的插頭,插座上有2個(gè)口的插頭。


這個(gè)例子跟我們編程一樣,當(dāng)用戶系統(tǒng)的接口跟我們系統(tǒng)內(nèi)部的接口不一致時(shí),我們可以使用適配器來(lái)完成接口的轉(zhuǎn)換。


使用繼承的方式實(shí)現(xiàn)類的適配:


public class Source {

    public void method() {

        System.out.println('source method');

    }

}

interface Targetable {

    void method();

    void newMethod();

}

class Adapter extends Source implements Targetable {

    @Override

    public void newMethod() {

        System.out.println('new method');

    }

}


測(cè)試:


Targetable targetable = new Adapter();

targetable.method(); // source method

targetable.newMethod(); // new method


上述方式是用接口和繼承的方式實(shí)現(xiàn)適配器模式。當(dāng)然我們也可以使用組合的方式實(shí)現(xiàn)(把Source當(dāng)成屬性放到Adapter中)。


單例模式


單例模式比較好理解,Spring就是典型的例子。被Spring中的容器管理的對(duì)象都有對(duì)應(yīng)的scope,配置成singleton說(shuō)明這個(gè)對(duì)象就是單例,也就是在Spring容器的生命周期中,這個(gè)類只有1個(gè)實(shí)例。


java中單例模式的寫法也有好多種。比如懶漢式、餓漢式、內(nèi)部類方式、枚舉方式等。


需要注意的如果使用dcl的話需要初始化過(guò)程,這篇Java內(nèi)存模型之從JMM角度分析DCL文章中說(shuō)明了dcl的正確用法。


Effectice java中推薦的單例方式寫法是使用枚舉類型的方式。


外觀模式


外觀模式用來(lái)包裝一組接口用于方便使用。 比如系統(tǒng)中分10個(gè)模塊,有個(gè)功能需要組合使用所有的模塊,這個(gè)時(shí)候就需要一個(gè)包裝類包裝這10個(gè)接口,然后進(jìn)行業(yè)務(wù)邏輯的調(diào)用。


看完本文有收獲?請(qǐng)轉(zhuǎn)發(fā)分享給更多人

關(guān)注「ImportNew」,看技術(shù)干貨

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多