乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      記錄自己理解的一些設計模式

       釋皇天 2017-04-23


      來源:fangjian0423,

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

      如有好文章投稿,請點擊 → 這里了解詳情


      記錄一下自己理解的一些設計模式,并盡量使用表達清楚的例子進行講解。


      策略模式


      策略模式應該是最基礎的一個設計模式,它是對行為的一個抽象。jdk中的Comparator比較器就是一個使用策略設計模式的策略。


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


      工廠模式


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


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


      比如有個Repository接口,用于存儲數據,有DatabaseRepository,CacheRepository,FileRepository分別在數據庫,緩存,文件中存儲數據,定義如下:


      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');

          }

      }


      簡單工廠的使用


      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());

          }

      }


      簡單工廠的弊端在于每添加一個新的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();

          }

      }


      抽象工廠的測試:


      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());


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


      裝飾者模式


      裝飾者模式的作用就在于它可以在不改變原有類的基礎上動態(tài)地給類添加新的功能。之前寫過一篇通過源碼分析MyBatis的緩存文章,mybatis中的query就是使用了裝飾者設計模式。


      用一段簡單的代碼來模擬一下mybatis中query的實現原理:


      @Data

      @AllArgsConstructor

      @ToString

      class Result { // 查詢結果類,相當于一個domain

        private Object obj;

        private String sql;

      }

       

      public interface Query { // 查詢接口,有簡單查詢和緩存查詢

        Result query(String sql);

      }

       

      public class SimpleQuery implements Query { // 簡單查詢,相當于直接查詢數據庫,這里直接返回Result,相當于是數據庫查詢的結果

        @Override

        public Result query(String sql) {

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

        }

      }

       

      public class CacheQuery implements Query { // 緩存查詢,如果查詢相同的sql,不直接查詢數據庫,而是返回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;

        }

      }


      測試:


      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就是一個裝飾類,SimpleQuery是一個被裝飾者。我們通過裝飾者設計模式動態(tài)地給SimpleQuery添加了緩存功能,而不需要修改SimpleQuery的代碼。


      當然,裝飾者模式也有缺點,就是會存在太多的類。


      如果我們需要添加一個過濾的查詢(sql中有敏感字的就直接返回null,而不查詢數據庫),只需要可以添加一個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')


      代理模式


      代理模式的作用是使用一個代理類來代替原先類進行操作。比較常見的就是aop中就是使用代理模式完成事務的處理。


      代理模式分靜態(tài)代理和動態(tài)代理,靜態(tài)代理的原理就是對目標對象進行封裝,最后調用目標對象的方法即可。


      動態(tài)代理跟靜態(tài)代理的區(qū)別就是動態(tài)代理中的代理類是程序運行的時候生成的。Spring中對于接口的代理使用jdk內置的Proxy和InvocationHandler實現,對于類的代理使用cglib完成。


      以1個UserService為例,使用jdk自帶的代理模式完成計算方法調用時間的需求:


      // UserService接口

      public interface IUserService {

          void printAll();

      }

      // UserService實現類

      class UserService implements IUserService {

          @Override

          public void printAll() {

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

          }

      }

      // InvocationHandler策略,這里打印了方法調用前后的時間

      @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;

          }

      }


      測試:


      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


      組合模式


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


      在HandlerMethodReturnValueHandlerComposite類中,使用存在的HandlerMethodReturnValueHandler對返回值進行處理,在HandlerMethodReturnValueHandlerComposite內部的代碼如下:


      // 策略集合

      private final List returnValueHandlers = new ArrayList();

       

      @Override

      public void handleReturnValue(Object returnValue, MethodParameter returnType,

          ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

          // 調用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進行處理

      }

       

      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;

      }


      模板模式


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


      一段偽代碼:


      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 { // 取出年紀最大的學生

          @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 { // 按照名字字母排序取出第一個學生

          @Override

          public void sort(List students) {

              students.sort(new Comparator() {

                  @Override

                  public int compare(Student s1, Student s2) {

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

                  }

              });

          }

      }


      測試:


      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)


      觀察者設計模式


      觀察者設計模式主要的使用場景在于一個對象變化之后,依賴該對象的對象會收到通知。典型的例子就是rss的訂閱,當訂閱了博客的rss之后,當博客更新之后,訂閱者就會收到新的訂閱信息。


      jdk內置提供了Observable和Observer,用來實現觀察者模式:


      // 定義一個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);

          }

      }


      測試:


      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}


      享元模式


      線程池中會構造幾個核心線程用于處理,這些線程會去取阻塞隊列里的任務然后進行執(zhí)行。這些線程就是會被共享、且被重復使用的。因為線程的創(chuàng)建、銷毀、調度都是需要消耗資源的,沒有必要每次創(chuàng)建新的線程,而是共用一些線程。這就是享元模式的使用。類似的還有jdbc連接池,對象池等。


      之前有一次面試被問到:


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


      當時回答的是false,后來翻了下Integer的源碼發(fā)現Integer里面有個內部類IntegerCache,用于緩存一些共用的Integer。這個緩存的范圍可以在jvm啟動的時候進行設置。


      其實后來想想也應該這么做,我們沒有必要每次使用對象的時候都返回新的對象,可以共享這些對象,因為新對象的創(chuàng)建都是需要消耗內存的。


      適配器模式


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


      這個例子跟我們編程一樣,當用戶系統(tǒng)的接口跟我們系統(tǒng)內部的接口不一致時,我們可以使用適配器來完成接口的轉換。


      使用繼承的方式實現類的適配:


      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');

          }

      }


      測試:


      Targetable targetable = new Adapter();

      targetable.method(); // source method

      targetable.newMethod(); // new method


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


      單例模式


      單例模式比較好理解,Spring就是典型的例子。被Spring中的容器管理的對象都有對應的scope,配置成singleton說明這個對象就是單例,也就是在Spring容器的生命周期中,這個類只有1個實例。


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


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


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


      外觀模式


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


      看完本文有收獲?請轉發(fā)分享給更多人

      關注「ImportNew」,看技術干貨

        本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現有害或侵權內容,請點擊一鍵舉報。
        轉藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多