小程式≠微信小程式 說到小程式,大部分同學的第一反應,可能是微信小程式、支付寶小程式,確實,小程式的概念深入人心,並且已經被約定俗成的綁定到某些互聯網公司的 APP 上。 但是,“小程式”並不是一個註冊商標,也不是哪一家的專利。 小程式作為一種人機交互的軟體載體、一種數字內容格式、一種代碼分發傳播機 ...
註:所有知識來源於《設計模式:可復用軟體面向對象的基礎》
創建型設計模式抽象了實例化過程,它們幫助一個系統獨立於如何創建、組合和表示它的那些對象。一個類創建型模式使用繼承改變被實例化的類,而一個對象創建型模式將實例化委托給另一個對象。
在這些模式中有兩個不斷出現的主旋律:
- 它們都將關於該系統使用哪些具體的類的信息封裝起來。
- 它們隱藏了這些類的實例是如何被創建和放在一起的。
整個系統關於這些對象所知道的是由抽象類所定義的介面。
示例—創建迷宮
我們以為游戲創建一個迷宮作為學習創建型模式的例子,忽略迷宮中的許多細節以及一個迷宮游戲中有多少游戲者,僅關註迷宮是怎麼創建的。我們將一個迷宮定義為一系列房間,一個房間知道它的鄰居:可能的鄰居要麼是另一個房間,要麼是一堵牆或者是到另一個房間的一扇門。
在該例子中,需要使用到的作為迷宮構件的類有Room、Door和Wall,註意我們只關註怎樣創建迷宮,忽略游戲者、顯示操作和在迷宮中四處移動等操作,以及其他一些重要的卻與創建迷宮無關的功能。
圖1 迷宮構件的類圖
- 每一個Room有四面,用枚舉類Direction來表示。
public enum Direction {
NORTH,
SOUTH,
EAST,
West
}
- 類MapSite是所有迷宮構件的抽象類,其中只定義了一個操作enter(),其含義取決於進入哪裡:如果進入一個房間,那麼位置發生改變;如果進入一扇門,若門開著則進入另一房間且位置改變,若門關著則碰壁且位置不變。
public abstract class MapSite {
public abstract void enter();
}
- 類Room是MapSite的一個具體子類,而MapSite定義了迷宮中構件之間的主要關係,Room有指向其他MapSite對象的引用,並保存一個房間號用來表示迷宮種的房間,sites是指當前房間四個方向所相鄰的構件。
public class Room extends MapSite {
private int roomNumber;
private Map<String, MapSite> sites;
public Room(int roomNumber) {
this.roomNumber = roomNumber;
this.sites = new HashMap<>(4);
}
public MapSite getSide(Direction direction) {
return sites.get(direction.getDirection());
}
public void setSites(Direction direction, MapSite mapSite) {
sites.put(direction.getDirection(), mapSite);
}
public int getRoomNumber() {
return this.roomNumber;
}
@Override
public void enter() {
System.out.println("Enter another room and position change.");
}
}
- 類Wall是可能出現在房間四面的牆。
public class Wall extends MapSite {
@Override
public void enter() {
System.out.println("It's a wall. Position not change.");
}
}
- 類Door是可能出現在房間四面的門,它連接了兩個房間,同時它本身可能是開著的也可能是關著的。
public class Door extends MapSite {
private Room room1;
private Room room2;
boolean isOpen;
public Door(Room room1, Room room2, boolean isOpen) {
this.room1 = room1;
this.room2 = room2;
this.isOpen = isOpen;
}
@Override
public void enter() {
if (isOpen) {
System.out.println("The door is opened. Enter another room and position change.");
} else {
System.out.println("The door is closed. Position not change.");
}
}
}
- 有了迷宮的各個構件後就可以組成迷宮了,類Maze就是迷宮,迷宮是由各個房間組成的,通過getRoom操作和給定的房間號,Maze就能找到特定的房間,此處採用HashMap類保存房間集合。
public class Maze {
Map<Integer, Room> rooms = new HashMap<>();
public void addRoom(Room room) {
rooms.put(room.getRoomNumber(), room);
}
public Room getRoom(int roomNumber) {
return rooms.get(roomNumber);
}
}
- 當我們想構建一個只有兩個迷宮且這兩個迷宮是通過一扇門來連接的時候,其創建操作如下:
public class MazeGame {
public Maze createMaze() {
Maze maze = new Maze();
Room room1 = new Room(1);
Room room2 = new Room(2);
Door door = new Door(room1, room2, true);
maze.addRoom(room1);
maze.addRoom(room2);
room1.setSites(Direction.NORTH, new Wall());
room1.setSites(Direction.SOUTH, new Wall());
room1.setSites(Direction.EAST, door);
room1.setSites(Direction.West, new Wall());
room2.setSites(Direction.NORTH, new Wall());
room2.setSites(Direction.SOUTH, new Wall());
room2.setSites(Direction.EAST, new Wall());
room2.setSites(Direction.West, door);
return maze;
}
}
考慮到這創建的是一個只有兩個房間的迷宮就已經顯得相當複雜了,雖然可以提前在Room構造器里用牆壁初始化房間的每一面,但是這隻是把代碼移到了其它地方。這個方法真正的問題不在於它的大小而在於它不靈活,它對迷宮的佈局進行硬編碼。改變佈局意味著改變這個成員方法,通過以下方式定義:重定義(override)它——意味著重新實現整個過程;對它的部分進行改變——容易產生錯誤並且不利於復用。
這種情況下改變的最大障礙是對以實例化的類進行硬編碼,創建型模式就提供了多種不同的方法,從實例化它們的代碼中去除對這些具體類的顯示引用。
- 如果createMaze調用抽象方法或介面而不是構造器來創建它需要的房間、牆壁和門,那麼可以創建一個MazeGame的子類並重寫這些方法或介面,從而改變被實例化的類。這一方法是Factory Method模式的使用。
- 如果傳遞一個對象給createMaze作為參數來創建房間、牆壁和門,那麼可以傳遞不同的參數來改變房間、牆壁和門的類。這是Abstract Factory模式的使用。
- 如果傳遞一個對象給createMaze,這個對象可以在它所建造的迷宮中使用增加房間、牆壁和門的操作來全面創建一個新的迷宮,那麼可以使用繼承來改變迷宮的一些部分或迷宮的建造方式。這是Builder模式的使用。
- 如果createMaze由多種原型的房間、牆壁和門對象參數化,它複製並將這些對象增加到迷宮中,那麼可以用不同的對象替換這些原型對象以改變迷宮的構成。這是Prorotype模式的使用。
剩下的Singleton模式可以保證每個游戲中僅有一個迷宮而且所有的游戲對象都可以迅速訪問它——不需要求助於全局變數或方法。Singleton也使得迷宮易於擴展或替換且不需要變動已有的代碼。