zoukankan      html  css  js  c++  java
  • 设计模式之美学习-行为型-策略模式(二十八)

    什么是策略模式

    定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。策略模式可以使算法的变化独立于使用它们的客户端(这里的客户端代指使用算法的代码

    策略模式。在实际的项目开发中,这个模式也比较常用。最常见的应用场景是,利用它来避免冗长的 if-else 或 switch 分支判断。不过,它的作用还不止如此。它也可以像模板模式那样,提供框架的扩展点等等。

    标准实现

    //策略抽象接口
    public interface Strategy {
        void algorithmInterface();
    }
    
    //策略1
    public class ConcreteStrategyA implements Strategy {
        @Override
        public void  algorithmInterface() {
            //具体的算法...
        }
    }
    
    //策略2
    public class ConcreteStrategyB implements Strategy {
        @Override
        public void  algorithmInterface() {
            //具体的算法...
        }
    }

    策略工厂 无状态对象

    public class StrategyFactory {
      private static final Map<String, Strategy> strategies = new HashMap<>();
    
      static {
        strategies.put("A", new ConcreteStrategyA());
        strategies.put("B", new ConcreteStrategyB());
      }
    
      public static Strategy getStrategy(String type) {
        if (type == null || type.isEmpty()) {
          throw new IllegalArgumentException("type should not be empty.");
        }
        return strategies.get(type);
      }
    }

    策略工厂有状态

    public class StrategyFactory {
      public static Strategy getStrategy(String type) {
        if (type == null || type.isEmpty()) {
          throw new IllegalArgumentException("type should not be empty.");
        }
    
        if (type.equals("A")) {
          return new ConcreteStrategyA();
        } else if (type.equals("B")) {
          return new ConcreteStrategyB();
        }
    
        return null;
      }
    }

    使用

    // 策略接口:EvictionStrategy
    // 策略类:LruEvictionStrategy、FifoEvictionStrategy、LfuEvictionStrategy...
    // 策略工厂:EvictionStrategyFactory
    
    public class UserCache {
        private Map<String, User> cacheData = new HashMap<>();
        private EvictionStrategy eviction;
    
        public UserCache(EvictionStrategy eviction) {
            this.eviction = eviction;
        }
    
        //...
    }
    
    // 运行时动态确定,根据配置文件的配置决定使用哪种策略
    public class Application {
        public static void main(String[] args) throws Exception {
            EvictionStrategy evictionStrategy = null;
            Properties props = new Properties();
            props.load(new FileInputStream("./config.properties"));
            String type = props.getProperty("eviction_type");
            //通过配置文件获取策略
            evictionStrategy = EvictionStrategyFactory.getEvictionStrategy(type);
            //构造函数注入
            UserCache userCache = new UserCache(evictionStrategy);
            //...
        }
    }
    
    // 非运行时动态确定,在代码中指定使用哪种策略
    public class Application {
        public static void main(String[] args) {
            //... 直接指定策略
            EvictionStrategy evictionStrategy = new LruEvictionStrategy();
            //构造函数注入
            UserCache userCache = new UserCache(evictionStrategy);
            //...
        }
    }

    策略模式实战

    需求

    希望写一个小程序,实现对一个文件进行排序的功能。文件中只包含整型数,并且,相邻的数字通过逗号来区隔。如果由你来编写这样一个小程序,你会如何来实现呢

    最初实现

    public class Sorter {
      private static final long GB = 1000 * 1000 * 1000;
    
      public void sortFile(String filePath) {
        // 省略校验逻辑
        File file = new File(filePath);
        long fileSize = file.length();
        if (fileSize < 6 * GB) { // [0, 6GB)
          quickSort(filePath);
        } else if (fileSize < 10 * GB) { // [6GB, 10GB)
          externalSort(filePath);
        } else if (fileSize < 100 * GB) { // [10GB, 100GB)
          concurrentExternalSort(filePath);
        } else { // [100GB, ~)
          mapreduceSort(filePath);
        }
      }
    
      private void quickSort(String filePath) {
        // 快速排序
      }
    
      private void externalSort(String filePath) {
        // 外部排序
      }
    
      private void concurrentExternalSort(String filePath) {
        // 多线程外部排序
      }
    
      private void mapreduceSort(String filePath) {
        // 利用MapReduce多机排序
      }
    }
    
    public class SortingTool {
      public static void main(String[] args) {
        Sorter sorter = new Sorter();
        sorter.sortFile(args[0]);
      }
    }

    “编码规范” 函数的行数不能过多,最好不要超过一屏的大小。所以,为了避免 sortFile() 函数过长,我们把每种排序算法从 sortFile() 函数中抽离出来,拆分成 4 个独立的排序函数。

    重构一

    那就是将 Sorter 类中的某些代码拆分出来,独立成职责更加单一的小类

    public interface ISortAlg {
      void sort(String filePath);
    }
    
    public class QuickSort implements ISortAlg {
      @Override
      public void sort(String filePath) {
        //...
      }
    }
    
    public class ExternalSort implements ISortAlg {
      @Override
      public void sort(String filePath) {
        //...
      }
    }
    
    public class ConcurrentExternalSort implements ISortAlg {
      @Override
      public void sort(String filePath) {
        //...
      }
    }
    
    public class MapReduceSort implements ISortAlg {
      @Override
      public void sort(String filePath) {
        //...
      }
    }
    
    public class Sorter {
      private static final long GB = 1000 * 1000 * 1000;
    
      public void sortFile(String filePath) {
        // 省略校验逻辑
        File file = new File(filePath);
        long fileSize = file.length();
        ISortAlg sortAlg;
        if (fileSize < 6 * GB) { // [0, 6GB)
          sortAlg = new QuickSort();
        } else if (fileSize < 10 * GB) { // [6GB, 10GB)
          sortAlg = new ExternalSort();
        } else if (fileSize < 100 * GB) { // [10GB, 100GB)
          sortAlg = new ConcurrentExternalSort();
        } else { // [100GB, ~)
          sortAlg = new MapReduceSort();
        }
        sortAlg.sort(filePath);
      }
    }

    重构二

    经过拆分之后,每个类的代码都不会太多,每个类的逻辑都不会太复杂,代码的可读性、可维护性提高了。除此之外,我们将排序算法设计成独立的类,跟具体的业务逻辑(代码中的 if-else 那部分逻辑)解耦,也让排序算法能够复用。这一步实际上就是策略模式的第一步,也就是将策略的定义分离出来

    public class SortAlgFactory {
      private static final Map<String, ISortAlg> algs = new HashMap<>();
    
      static {
        algs.put("QuickSort", new QuickSort());
        algs.put("ExternalSort", new ExternalSort());
        algs.put("ConcurrentExternalSort", new ConcurrentExternalSort());
        algs.put("MapReduceSort", new MapReduceSort());
      }
    
      public static ISortAlg getSortAlg(String type) {
        if (type == null || type.isEmpty()) {
          throw new IllegalArgumentException("type should not be empty.");
        }
        return algs.get(type);
      }
    }
    
    public class Sorter {
      private static final long GB = 1000 * 1000 * 1000;
    
      public void sortFile(String filePath) {
        // 省略校验逻辑
        File file = new File(filePath);
        long fileSize = file.length();
        ISortAlg sortAlg;
        if (fileSize < 6 * GB) { // [0, 6GB)
          sortAlg = SortAlgFactory.getSortAlg("QuickSort");
        } else if (fileSize < 10 * GB) { // [6GB, 10GB)
          sortAlg = SortAlgFactory.getSortAlg("ExternalSort");
        } else if (fileSize < 100 * GB) { // [10GB, 100GB)
          sortAlg = SortAlgFactory.getSortAlg("ConcurrentExternalSort");
        } else { // [100GB, ~)
          sortAlg = SortAlgFactory.getSortAlg("MapReduceSort");
        }
        sortAlg.sort(filePath);
      }
    }

    重构三

    Sorter 类中的 sortFile() 函数还是有一堆 if-else 逻辑。这里的 if-else 逻辑分支不多、也不复杂,这样写完全没问题。但如果你特别想将 if-else 分支判断移除掉,那也是有办法的。我直接给出代码,你一看就能明白。实际上,这也是基于查表法来解决的,其中的“algs”就是“表”。

    public class Sorter {
      private static final long GB = 1000 * 1000 * 1000;
      private static final List<AlgRange> algs = new ArrayList<>();
      static {
        algs.add(new AlgRange(0, 6*GB, SortAlgFactory.getSortAlg("QuickSort")));
        algs.add(new AlgRange(6*GB, 10*GB, SortAlgFactory.getSortAlg("ExternalSort")));
        algs.add(new AlgRange(10*GB, 100*GB, SortAlgFactory.getSortAlg("ConcurrentExternalSort")));
        algs.add(new AlgRange(100*GB, Long.MAX_VALUE, SortAlgFactory.getSortAlg("MapReduceSort")));
      }
    
      public void sortFile(String filePath) {
        // 省略校验逻辑
        File file = new File(filePath);
        long fileSize = file.length();
        ISortAlg sortAlg = null;
        for (AlgRange algRange : algs) {
          if (algRange.inRange(fileSize)) {
            sortAlg = algRange.getAlg();
            break;
          }
        }
        sortAlg.sort(filePath);
      }
    
      private static class AlgRange {
        private long start;
        private long end;
        private ISortAlg alg;
    
        public AlgRange(long start, long end, ISortAlg alg) {
          this.start = start;
          this.end = end;
          this.alg = alg;
        }
    
        public ISortAlg getAlg() {
          return alg;
        }
    
        public boolean inRange(long size) {
          return size >= start && size < end;
        }
      }
    }
  • 相关阅读:
    如何缓解DDOS攻击
    centos-linux热拔插scsi硬盘
    AWS CLI以及AWS S3 SYNC命令行使用
    CentOS安装VMware Tools
    在VM克隆CENTOS以后,网卡的处理过程
    Centos 的计划任务 crontab
    Windows7无法访问共享文件夹(0x800704cf,0x80070035)解决方法
    Javascript遍历页面控件
    总结一些js自定义的函数
    JS应用(资料很全)
  • 原文地址:https://www.cnblogs.com/LQBlog/p/12672906.html
Copyright © 2011-2022 走看看