注:所有知識來源于《設計模式:可復用軟體面向物件的基礎》
創建型設計模式抽象了實體化程序,它們幫助一個系統獨立于如何創建、組合和表示它的那些物件,一個類創建型模式使用繼承改變被實體化的類,而一個物件創建型模式將實體化委托給另一個物件,
在這些模式中有兩個不斷出現的主旋律:
- 它們都將關于該系統使用哪些具體的類的資訊封裝起來,
- 它們隱藏了這些類的實體是如何被創建和放在一起的,
整個系統關于這些物件所知道的是由抽象類所定義的介面,
示例—創建迷宮
我們以為游戲創建一個迷宮作為學習創建型模式的例子,忽略迷宮中的許多細節以及一個迷宮游戲中有多少游戲者,僅關注迷宮是怎么創建的,我們將一個迷宮定義為一系列房間,一個房間知道它的鄰居:可能的鄰居要么是另一個房間,要么是一堵墻或者是到另一個房間的一扇門,
在該例子中,需要使用到的作為迷宮構件的類有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也使得迷宮易于擴展或替換且不需要變動已有的代碼,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/541310.html
標籤:設計模式
