1. 程式人生 > 程式設計 >Pytorch 卷積中的 Input Shape用法

Pytorch 卷積中的 Input Shape用法

策略模式是GoF23種設計模式中比較簡單的了,也是常用的設計模式之一,今天我們就來看看策略模式。

實際案例

我工作第三年的時候,重構旅遊路線的機票查詢模組,旅遊路線分為四種情況:

  • 如果A地-B地往返都可以直達,那麼查詢兩張機票(往返)

  • 如果A地-B地去程無法直達,需要中轉,但是返程可以直達,那麼查詢三張機票(去程兩張,返程一張)

  • 如果A地-B地去程可以直達,但是返程需要中轉,那麼查詢三張機票(去程一張,返程兩張)

  • 如果A地-B地往返都無法直達,那麼查詢四張機票(去程兩張,返程兩張)

在我重構前,程式碼差不多是這樣的:

         int type = 1;
         
// 往返都可以直達 if (type == 1) { // 查詢出兩張機票 return; } ​ // 去程無法直達,需要中轉,但是返程可以直達 if (type == 2) { // 查詢出三張機票(去程兩張,返程一張) return; } // 去程可以直達,但是返程需要中轉 if (type == 3) { // 查詢出三張機票(去程一張,返程兩張) return
; } // 往返都無法直達 else{ // 查詢出四張機票(去程兩張,返程兩張) return; }


當時我還是菜雞(現在也是),也不懂什麼設計模式,就是感覺程式碼都寫在一個類中,實在是太長了,不夠清爽,不管是哪種型別的線路,最終都是返回機票集合,只是處理邏輯不同,可以提取一個接口出來,再開四個類去實現此介面,最後定義一個Map,Key是Type,Value是介面(實現類),根據Type決定呼叫哪個實現類,就像下面的醬紫:

 public class Ticket {
     
private String desc; ​ public Ticket(String desc) { this.desc = desc; } ​ public String getDesc() { return desc; } ​ public void setDesc(String desc) { this.desc = desc; } ​ @Override public String toString() { return "Ticket{" + "desc='" + desc + '\'' + '}'; } } ​ public interface QueryTicketService { List<Ticket> getTicketList(); } ​ public class QueryTicketAService implements QueryTicketService { @Override public List<Ticket> getTicketList() { List<Ticket> list = new ArrayList<>(); list.add(new Ticket("去程機票")); list.add(new Ticket("返程機票")); return list; } } ​ public class QueryTicketBService implements QueryTicketService { @Override public List<Ticket> getTicketList() { List<Ticket> list = new ArrayList<>(); list.add(new Ticket("去程第一張機票")); list.add(new Ticket("去程第二張機票")); list.add(new Ticket("返程機票")); return list; } } ​ public class QueryTicketCService implements QueryTicketService { @Override public List<Ticket> getTicketList() { List<Ticket> list = new ArrayList<>(); list.add(new Ticket("去程機票")); list.add(new Ticket("返程第一張機票")); list.add(new Ticket("返程第二張機票")); return list; } } ​ public class QueryTicketDService implements QueryTicketService { @Override public List<Ticket> getTicketList() { List<Ticket> list = new ArrayList<>(); list.add(new Ticket("去程第一張機票")); list.add(new Ticket("去程第二張機票")); list.add(new Ticket("返程第一張機票")); list.add(new Ticket("返程第二張機票")); return list; } } ​ public class Main { static Map<Integer, QueryTicketService> map = new HashMap<>(); ​ static { map.put(1, new QueryTicketAService()); map.put(2, new QueryTicketBService()); map.put(3, new QueryTicketCService()); map.put(4, new QueryTicketDService()); } ​ public static void main(String[] args) { int type = 1; System.out.println(map.get(type).getTicketList()); } }


執行結果:

 [Ticket{desc='去程機票'}, Ticket{desc='返程機票'}]


當初我也不知道什麼設計模式,就是感覺這樣寫完,程式碼清爽多了,後來才知道這就是策略模式的雛形了。

GoF23種設計模式真正應用廣泛的設計模式不多,但是策略模式絕對算其中之一了,你看,當初我都不懂這些,就寫出了策略模式的雛形。

原始的策略模式

如果我們遇到類似於上面的需求,第一反應肯定是用if else語句或者switch語句,根據不同的情況執行不同的程式碼,這樣做也沒什麼大問題,但是我們的專案會越來越複雜,這麼做的缺陷就慢慢的顯現了出來:如果現線上路新增了一個型別,需要中轉兩次,就又得加好幾個判斷的分支(去程中轉一次,返程中轉兩次;去程中轉兩次,返程中轉一次;去程直達,返程中轉兩次等等),想想就恐怖,這樣分支會越來越多,程式碼會越來越長,越來越難以維護,所以策略模式出現了。

當一個邏輯中,有很多if else語句或者switch語句,而且它們需要解決的問題是一樣的,就可以考慮策略模式。

最原始的策略模式有三個角色:

  • Strategy:抽象策略角色,對演算法、策略的抽象,定義每個演算法、策略所必需的方法,通常為介面。

  • ConcreteStrategy:具體策略角色,實現抽象策略角色,完成具體的演算法、策略。

  • Context:上下文環境角色,儲存了ConcreteStrategy,負責呼叫ConcreteStrategy。

而我上面的程式碼,就有了策略模式的味道,有了Strategy,也有了ConcreteStrategy,缺少的就是Context,如果用最原始的設計模式的寫法來實現,是醬紫的:



public class Context {
     static Map<Integer, QueryTicketStrategy> map = new HashMap<>();
 ​
     static {
         map.put(1, new QueryTicketAConcreteStrategy());
         map.put(2, new QueryTicketBConcreteStrategy());
         map.put(3, new QueryTicketCConcreteStrategy());
         map.put(4, new QueryTicketDConcreteStrategy());
     }
 ​
     public void getTicketList(int type) {
         System.out.println(map.get(type).getTicketList());
     }
 }
 ​
 public class Main {
     public static void main(String[] args) {
         Context context = new Context();
         context.getTicketList(1);
     }
 }


執行結果:

 [Ticket{desc='去程機票'}, Ticket{desc='返程機票'}]


在這裡,我把類名重新定義了下,讓人一眼就可以看出這裡使用了策略模式,這也是阿里推薦的命名方法。

策略模式是不是很簡單(我在學習設計模式的時候,甚至覺得它比單例、簡單工廠還要簡單),而且特別實用,下面我們來看看策略模式的UML圖:

JDK中的策略模式

既然策略模式那麼實用,那麼在JDK中有策略模式的應用嗎?當然有。JDK中定義的Comparator介面就是策略模式的一種實踐了:

 public class SortLengthComparator implements Comparator<String> {
     @Override
     public int compare(String o1, String o2) {
         return (o1.length() - o2.length() > 0) ? 1 : -1;
     }
 }
 ​
 public class Main {
     public static void main(String[] args) {
         List<String>list=new ArrayList<>();
         list.add("hello");
         list.add("world");
         list.add("codebear");
         list.add("balabala");
         list.add("java");
         list.sort(new SortLengthComparator());
         System.out.println(list);
     }
 }


我定義了一個比較器,實現了Comparator介面,重寫了compare方法,實現了以比較字串長度來比較字串的功能。

執行結果:

 [java, world, hello, balabala, codebear]


Comparator介面就是Strategy,我定義的SortLengthComparator就是ConcreteStrategy。

Comparator結合Lambda,會產生怎樣的火花

定義一個比較器,雖然不難,但是總覺得不夠簡潔,不夠方便,需要新建一個類,所以現在越來越多的人使用Lambda來進行排序,就像下面的醬紫:

         List<String>list=new ArrayList<>();
         list.add("hello");
         list.add("world");
         list.add("codebear");
         list.add("balabala");
         list.add("java");
         List<String> newList = list.stream().sorted((a, b) -> (a.length() - b.length() > 0) ? 1 : -1).collect(Collectors.toList());
         newList.forEach(System.out::println);


雖然底層還是用的Comparator,但是這樣的寫法清爽多了,如果比較的策略比較複雜,或者有多個地方都需要用到這個比較策略,還是用最原始的寫法更好一些。

策略模式與Spring的碰撞

現在我們已經知道了什麼是策略模式,如何使用策略模式,但是還有一個天大的問題,要知道,現在每個專案都在用Spring,如果你還是這麼寫的話:

 public class Context {
     static Map<Integer, QueryTicketStrategy> map = new HashMap<>();
 ​
     static {
         map.put(1, new QueryTicketAConcreteStrategy());
         map.put(2, new QueryTicketBConcreteStrategy());
         map.put(3, new QueryTicketCConcreteStrategy());
         map.put(4, new QueryTicketDConcreteStrategy());
     }
 ​
     public void getTicketList(int type) {
         System.out.println(map.get(type).getTicketList());
     }
 }


就意味著實現類裡面的依賴需要自己去維護,無法使用神奇的@Autowired註解,所以策略模式與Spring碰撞,策略模式必須發生一點改變,而這改變讓策略模式變得更加簡單,效能更好,也更加迷人。

寫法1

 @Service
 public class QueryTicketAConcreteStrategy implements QueryTicketStrategy {
     @Override
     public List<Ticket> getTicketList() {
         List<Ticket> list = new ArrayList<>();
         list.add(new Ticket("去程機票"));
         list.add(new Ticket("返程機票"));
         return list;
     }
 }
 ​
 @Service
 public class QueryTicketDConcreteStrategy implements QueryTicketStrategy {
     @Override
     public List<Ticket> getTicketList() {
         List<Ticket> list = new ArrayList<>();
         list.add(new Ticket("去程第一張機票"));
         list.add(new Ticket("去程第二張機票"));
         list.add(new Ticket("返程第一張機票"));
         list.add(new Ticket("返程第二張機票"));
         return list;
     }
 }
 ​
 @Service
 public class Context {
 ​
     @Autowired
     private QueryTicketStrategy queryTicketAConcreteStrategy;
 ​
     @Autowired
     private QueryTicketStrategy queryTicketDConcreteStrategy;
 ​
     private static Map<Integer, QueryTicketStrategy> map = new HashMap<>();
 ​
     @PostConstruct
     public void init() {
         map.put(1, queryTicketAConcreteStrategy);
         map.put(4, queryTicketAConcreteStrategy);
     }
 ​
     public void getTicketList(int type) {
         System.out.println(map.get(type).getTicketList());
     }
 }
 ​
 @SpringBootApplication
 public class Main {
     public static void main(String[] args) {
         ConfigurableApplicationContext run = SpringApplication.run(Main.class, args);
         run.getBean(Context.class).getTicketList(1);
     }
 }
 ​

執行結果:

 [Ticket{desc='去程機票'}, Ticket{desc='返程機票'}]

原始的設計模式有一個缺點,不管是具體的策略實現類,還是上下文類,都不是單例模式,而我們的方法在大多數情況下是無狀態的,所以改成單例模式是非常合適的,而結合了Spring,我們完全不需要手寫單例模式,Spring就幫我們完成了。

寫法2(自認為最優雅)

不管是原始的策略模式,還是Spring與策略模式結合的第一種寫法,都沒有完全符合開閉原則,如果有新的策略引入,必須修改上下文類,往map裡面新增一組新的對映關係,而第二種寫法完美的解決了這個問題,而且讓策略模式變得非常優雅,下面直接放出程式碼:

 @Service("1")
 public class QueryTicketAConcreteStrategy implements QueryTicketStrategy {
     @Override
     public List<Ticket> getTicketList() {
         List<Ticket> list = new ArrayList<>();
         list.add(new Ticket("去程機票"));
         list.add(new Ticket("返程機票"));
         return list;
     }
 }
 ​
 @Service("4")
 public class QueryTicketDConcreteStrategy implements QueryTicketStrategy {
     @Override
     public List<Ticket> getTicketList() {
         List<Ticket> list = new ArrayList<>();
         list.add(new Ticket("去程第一張機票"));
         list.add(new Ticket("去程第二張機票"));
         list.add(new Ticket("返程第一張機票"));
         list.add(new Ticket("返程第二張機票"));
         return list;
     }
 }
 ​
 @Service
 public class Context {
 ​
     @Autowired
     private Map<String, QueryTicketStrategy> map = new HashMap<>();
 ​
 ​
     public void getTicketList(int type) {
         String typeStr = String.valueOf(type);
         System.out.println(map.get(typeStr).getTicketList());
     }
 }
 ​
 @SpringBootApplication
 public class Main {
     public static void main(String[] args) {
         ConfigurableApplicationContext run = SpringApplication.run(Main.class, args);
         run.getBean(Context.class).getTicketList(1);
     }
 }

執行結果:

 [Ticket{desc='去程機票'}, Ticket{desc='返程機票'}]

這就是Spring和神奇、迷人之處了,竟然可以自動注入map,key就是beanName,value就是介面(具體的實現類)。

用這種寫法不但完成了天然的單例模式,而且真正的符合了開閉原則,引入新的策略,完全不需要修改任何一行舊程式碼,自認為這種寫法是最優雅、最迷人的。

總結

文章是有點水,請輕噴