|
| 1 | +# **单例+简单工厂模式在项目设计中的应用** |
| 2 | + |
| 3 | +## 单例模式简介: |
| 4 | + |
| 5 | +单例模式(Singleton Pattern)是一种常用的软件设计模式,它保证一个类仅有一个实例,并提供该实例的全局访问点。这种模式通常用于管理共享资源、控制对资源的访问以及在多线程环境中避免创建多个对象实例导致的冲突和资源浪费。 |
| 6 | + |
| 7 | +### 单例模式的优点与缺点 |
| 8 | + |
| 9 | +**优点:** |
| 10 | +- 对资源的节约:对于频繁使用的对象,避免了对象的频繁创建和销毁。 |
| 11 | +- 全局访问点:提供了全局的访问点供外界获取唯一的实例。 |
| 12 | +- 确保单一实例:保证了系统中只有一个实例对象,有助于协调多个部分的操作。 |
| 13 | + |
| 14 | +**缺点:** |
| 15 | +- 延迟加载:无法通过引用传递进行依赖注入。 |
| 16 | +- 反序列化重新创建对象:默认的反序列化机制会破坏单例,需要特殊处理。 |
| 17 | +- 线程安全:在某些实现中可能需要处理多线程环境下的同步问题。 |
| 18 | +- 违反单一职责原则:单例类的职责除了维护自己的单一实例外,还要兼顾自己的业务逻辑。 |
| 19 | + |
| 20 | +## 简单工厂模式简介: |
| 21 | + |
| 22 | +简单工厂模式(Simple Factory Pattern)是一种创建型设计模式,它提供了一种创建对象的最佳方式。在这种模式中,一个类负责创建其他类的实例,而不需要使客户端代码与具体的类有所依赖。这样做的好处是,客户端代码与具体的类实现解耦,使得系统更加灵活,易于扩展和维护。 |
| 23 | + |
| 24 | +### 简单工厂模式的优点与缺点 |
| 25 | + |
| 26 | +**优点:** |
| 27 | + |
| 28 | +- 减少耦合:客户端代码不需要知道具体的产品类,只需要知道如何通过工厂获取产品。 |
| 29 | +- 易于扩展:当需要添加新的产品时,只需添加具体产品类,并在工厂类中添加相应的逻辑,而无需修改现有的客户端代码。 |
| 30 | +- 提高代码的可维护性:由于对象的创建和使用是分离的,这使得代码更加清晰,易于维护。 |
| 31 | + |
| 32 | +**缺点:** |
| 33 | + |
| 34 | +- 工厂类职责过重:随着产品种类的增加,工厂类的责任会变得越来越重,难以维护。 |
| 35 | +- 可抽象程度有限:如果产品类别之间差异很大,可能不容易找到一个通用的超级类或接口,这时简单工厂模式可能不适用。 |
| 36 | + |
| 37 | + |
| 38 | +## 单例+简单工厂模式在项目设计中的应用 |
| 39 | + |
| 40 | +在12306项目中,通过单例模式实现单例容器,存放共享资源,并提供全局访问点。通过简单工厂模式,实现座位检查方法实例的创建,提高代码的可读性和灵活性。 |
| 41 | +项目中的单例模式采用饿汉式生成单例容器,代码如下: |
| 42 | +#### 单例容器 饿汉式(静态常量) |
| 43 | + |
| 44 | +```java |
| 45 | +/** |
| 46 | + * 单例对象容器 |
| 47 | + */ |
| 48 | +@NoArgsConstructor(access = AccessLevel.PRIVATE) |
| 49 | +public final class Singleton { |
| 50 | + |
| 51 | + private static final ConcurrentHashMap<String, Object> SINGLE_OBJECT_POOL = new ConcurrentHashMap(); |
| 52 | + |
| 53 | + /** |
| 54 | + * 根据 key 获取单例对象 |
| 55 | + */ |
| 56 | + public static <T> T get(String key) { |
| 57 | + Object result = SINGLE_OBJECT_POOL.get(key); |
| 58 | + return result == null ? null : (T) result; |
| 59 | + } |
| 60 | + |
| 61 | + /** |
| 62 | + * 根据 key 获取单例对象 |
| 63 | + * |
| 64 | + * <p> 为空时,通过 supplier 构建单例对象并放入容器 |
| 65 | + */ |
| 66 | + public static <T> T get(String key, Supplier<T> supplier) { |
| 67 | + Object result = SINGLE_OBJECT_POOL.get(key); |
| 68 | + if (result == null && (result = supplier.get()) != null) { |
| 69 | + SINGLE_OBJECT_POOL.put(key, result); |
| 70 | + } |
| 71 | + return result != null ? (T) result : null; |
| 72 | + } |
| 73 | + |
| 74 | + /** |
| 75 | + * 对象放入容器 |
| 76 | + */ |
| 77 | + public static void put(Object value) { |
| 78 | + put(value.getClass().getName(), value); |
| 79 | + } |
| 80 | + |
| 81 | + /** |
| 82 | + * 对象放入容器 |
| 83 | + */ |
| 84 | + public static void put(String key, Object value) { |
| 85 | + SINGLE_OBJECT_POOL.put(key, value); |
| 86 | + } |
| 87 | +``` |
| 88 | +### 简单工厂 + 单例容器 实现座位通过BitMap检测抽象工厂 |
| 89 | +```java |
| 90 | +/** |
| 91 | + * 座位通过 BitMap 检测抽象工厂 |
| 92 | + * |
| 93 | + */ |
| 94 | +public abstract class BitMapCheckSeatStatusFactory { |
| 95 | + |
| 96 | + public static final String TRAIN_BUSINESS = "TRAIN_BUSINESS"; |
| 97 | + public static final String TRAIN_FIRST = "TRAIN_FIRST"; |
| 98 | + public static final String TRAIN_SECOND = "TRAIN_SECOND"; |
| 99 | + |
| 100 | + /** |
| 101 | + * 获取座位检查方法实例 |
| 102 | + * |
| 103 | + * @param mark 座位标识 |
| 104 | + * @return 座位检查类 |
| 105 | + */ |
| 106 | + public static BitMapCheckSeat getInstance(String mark) { |
| 107 | + BitMapCheckSeat instance = null; |
| 108 | + switch (mark) { |
| 109 | + case TRAIN_BUSINESS -> { |
| 110 | + instance = Singleton.get(TRAIN_BUSINESS); |
| 111 | + if (instance == null) { |
| 112 | + instance = new TrainBusinessCheckSeat(); |
| 113 | + Singleton.put(TRAIN_BUSINESS, instance); |
| 114 | + } |
| 115 | + } |
| 116 | + case TRAIN_FIRST -> { |
| 117 | + instance = Singleton.get(TRAIN_FIRST); |
| 118 | + if (instance == null) { |
| 119 | + instance = new TrainFirstCheckSeat(); |
| 120 | + Singleton.put(TRAIN_FIRST, instance); |
| 121 | + } |
| 122 | + } |
| 123 | + case TRAIN_SECOND -> { |
| 124 | + instance = Singleton.get(TRAIN_SECOND); |
| 125 | + if (instance == null) { |
| 126 | + instance = new TrainSecondCheckSeat(); |
| 127 | + Singleton.put(TRAIN_SECOND, instance); |
| 128 | + } |
| 129 | + } |
| 130 | + } |
| 131 | + return instance; |
| 132 | + } |
| 133 | +} |
| 134 | + |
| 135 | +``` |
| 136 | +### 购票场景中使用 |
| 137 | +```java |
| 138 | +private Pair<List<TrainPurchaseTicketRespDTO>, Boolean> findMatchSeats(SelectSeatDTO requestParam, List<String> trainCarriageList, List<Integer> trainStationCarriageRemainingTicket) { |
| 139 | + TrainSeatBaseDTO trainSeatBaseDTO = buildTrainSeatBaseDTO(requestParam); |
| 140 | + int chooseSeatSize = trainSeatBaseDTO.getChooseSeatList().size(); |
| 141 | + List<TrainPurchaseTicketRespDTO> actualResult = Lists.newArrayListWithCapacity(trainSeatBaseDTO.getPassengerSeatDetails().size()); |
| 142 | + BitMapCheckSeat instance = BitMapCheckSeatStatusFactory.getInstance(TRAIN_BUSINESS); |
| 143 | + HashMap<String, List<Pair<Integer, Integer>>> carriagesSeatMap = new HashMap<>(4); |
| 144 | + int passengersNumber = trainSeatBaseDTO.getPassengerSeatDetails().size(); |
| 145 | + for (int i = 0; i < trainStationCarriageRemainingTicket.size(); i++) { |
| 146 | + String carriagesNumber = trainCarriageList.get(i); |
| 147 | + List<String> listAvailableSeat = seatService.listAvailableSeat(trainSeatBaseDTO.getTrainId(), carriagesNumber, requestParam.getSeatType(), trainSeatBaseDTO.getDeparture(), trainSeatBaseDTO.getArrival()); |
| 148 | + int[][] actualSeats = new int[2][3]; |
| 149 | + for (int j = 1; j < 3; j++) { |
| 150 | + for (int k = 1; k < 4; k++) { |
| 151 | + actualSeats[j - 1][k - 1] = listAvailableSeat.contains("0" + j + SeatNumberUtil.convert(0, k)) ? 0 : 1; |
| 152 | + } |
| 153 | + } |
| 154 | + List<Pair<Integer, Integer>> vacantSeatList = CarriageVacantSeatCalculateUtil.buildCarriageVacantSeatList2(actualSeats, 2, 3); |
| 155 | + boolean isExists = instance.checkChooseSeat(trainSeatBaseDTO.getChooseSeatList(), actualSeats, SEAT_Y_INT); |
| 156 | + // 省略其他代码 |
| 157 | + } |
| 158 | +} |
| 159 | +``` |
| 160 | + |
| 161 | +### 结论 |
| 162 | + |
| 163 | +在实际应用中,应根据具体的应用场景选择合适的实现方式,并注意其可能带来的问题。正确地使用设计模式可以提高代码扩展性、灵活性和复用性,但如果过度使用或不当使用,也可能带来不必要的复杂性和潜在的问题。 |
0 commit comments