Singleton & Factory

[Musings]
今天开始了设计模式的滚动上手学习,来增强下整体的设计思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
package org.example.factory.playground;
/*
题目

场景描述:
假设你正在开发一个跨平台的UI组件库,需要支持Windows和macOS两种操作系统。UI组件包括按钮(Button)和文本框(TextBox)。

要求:

使用工厂模式创建单个UI组件
使用抽象工厂模式创建整套UI组件
实现以下类:

抽象产品:Button, TextBox
具体产品:WindowsButton, WindowsTextBox, MacButton, MacTextBox
工厂类:ButtonFactory, TextBoxFactory(工厂模式)
抽象工厂:UIFactory(抽象工厂模式)
具体工厂:WindowsUIFactory, MacUIFactory(抽象工厂模式)
请实现上述所有类,并编写一个客户端代码演示两种模式的使用。

*/

public class FactoryMain {
public static void main(String[] args) {
System.out.println("=== 工厂模式演示 ===");
// 工厂模式
ButtonFactory buttonFactory = new ButtonFactory();
TextBoxFactory textBoxFactory = new TextBoxFactory();
Button button = buttonFactory.createButton("windows");
TextBox textBox = textBoxFactory.createTextBox("windows");
button.click();
button.print(); // 补充print方法调用
textBox.input(); // 补充input方法调用
textBox.print();

System.out.println("\n=== 抽象工厂模式演示 ===");
// 抽象工厂模式
UIFactory macUIFactory = new MacUIFactory();
UIFactory windowsFactory = new WindowsUIFactory();

Button macButton = macUIFactory.createButton();
TextBox macTextBox = macUIFactory.createTextBox();
macButton.click();
macButton.print();
macTextBox.input();
macTextBox.print();

Button windowsButton = windowsFactory.createButton();
TextBox windowsTextBox = windowsFactory.createTextBox();
windowsButton.click();
windowsButton.print();
windowsTextBox.input();
windowsTextBox.print();
}
}

package org.example.factory.playground;

abstract class Button {
public void click() {
System.out.println("button clicked");
}

abstract void print();
}

package org.example.factory.playground;

class ButtonFactory {
public Button createButton(String type) {
switch (type) {
case "windows":
return new WindowsButton();
case "mac":
return new MacButton();
default:
return new WindowsButton();
}
}
}

package org.example.factory.playground;

class MacButton extends Button {
@Override
void print() {
System.out.println("Mac Button print!");
}

@Override
public void click() {
System.out.println("Mac button click!");
}
}

package org.example.factory.playground;

class MacTextBox extends TextBox {
@Override
void print() {
System.out.println("Mac TextBox input!");
}
}

package org.example.factory.playground;

public class MacUIFactory extends UIFactory{
@Override
Button createButton() {
return new MacButton();
}

@Override
TextBox createTextBox() {
return new MacTextBox();
}
}


package org.example.factory.playground;

abstract class TextBox {
public void input() {
System.out.println("input activate");
}

abstract void print();
}

package org.example.factory.playground;

class TextBoxFactory {
public TextBox createTextBox(String type) {
switch (type) {
case "windows":
return new WindowsTextBox();
case "mac":
return new MacTextBox();
default:
return new WindowsTextBox();
}
}
}

package org.example.factory.playground;

public abstract class UIFactory {
abstract Button createButton();
abstract TextBox createTextBox();
}


package org.example.factory.playground;

class WindowsButton extends Button {
@Override
void print() {
System.out.println("Windows Button print!");

}

@Override
public void click() {
System.out.println("Windows button click!");
}
}

package org.example.factory.playground;

class WindowsTextBox extends TextBox {
@Override
void print() {
System.out.println("WindowsTextBox input!");
}
}

package org.example.factory.playground;

public class WindowsUIFactory extends UIFactory {
@Override
Button createButton() {
return new WindowsButton();
}

@Override
TextBox createTextBox() {
return new WindowsTextBox();
}
}


这里包含了抽象工厂模式和工厂模式,主要区别可以这么记忆

  1. 工厂模式: 聚焦于产品,打个比方就是
    手机专卖店: iphone手机,huawei手机,xiaomi手机
    音箱专卖店: bose音箱, apple音箱,小爱音箱

  2. 抽象工厂: 聚焦于产品簇, 打个比方就是
    原木风家居: 原木风桌子,原木风椅子,原木风床

所以:

工厂模式适合: 产品种类相对独立,不需要强一致的风格
客户端需要灵活组合不同来源的产品

抽象工厂模式适合: 需要确保一系列产品的兼容性和一致性
产品间有强烈的主题/风格关联
约束用户只使用同一系列的产品

工厂模式的实际应用:*
// BeanFactory - 最经典的工厂模式
ApplicationContext context = new ClassPathXmlApplicationContext(“beans.xml”);
UserService userService = context.getBean(“userService”, UserService.class);

// Log4j2 / SLF4J
Logger logger = LoggerFactory.getLogger(MyClass.class);
// 根据配置返回Log4jLogger、JULLogger等具体实现

// JDBC DriverManager
Connection conn = DriverManager.getConnection(url, user, password);
// 根据URL返回MySQL、PostgreSQL、Oracle等具体连接

抽象工厂模式的实际应用
// JavaFX / Swing 的主题系统
// 你实现的例子就是最典型的应用!
UIFactory factory = Platform.getUIFactory();
Button btn = factory.createButton();
Menu menu = factory.createMenu();

// 不同数据库的DAO工厂
DAOFactory factory = DAOFactory.getFactory(DBType.MYSQL);
UserDAO userDAO = factory.getUserDAO();
OrderDAO orderDAO = factory.getOrderDAO();
// 保证所有DAO使用同一种数据库

// 不同消息中间件的工厂
MessageFactory factory = MessageFactory.getKafkaFactory();
Producer producer = factory.createProducer();
Consumer consumer = factory.createConsumer();
AdminClient admin = factory.createAdminClient();

// AWS / Azure / Google Cloud 的抽象工厂
CloudFactory factory = CloudFactory.getAWSFactory();
StorageService storage = factory.createStorageService();
ComputeService compute = factory.createComputeService();
DatabaseService db = factory.createDatabaseService();

第二个讨论下 Singleton

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class EagerSingleton {
// 类加载时就创建实例
private static instance: EagerSingleton = new EagerSingleton();

// 私有构造器
private constructor() {
console.log("EagerSingleton 实例被创建");
}

// 公共访问方法
public static getInstance(): EagerSingleton {
return EagerSingleton.instance;
}

public showMessage(): void {
console.log("Hello from EagerSingleton!");
}
}

// 使用
const eager1 = EagerSingleton.getInstance();
const eager2 = EagerSingleton.getInstance();
console.log(eager1 === eager2); // true,同一个实例

class LazySingleton {
private static instance: LazySingleton | null = null;

private constructor() {
console.log("LazySingleton 实例被创建");
}

// 简单的懒加载
public static getInstance(): LazySingleton {
if (LazySingleton.instance === null) {
LazySingleton.instance = new LazySingleton();
}
return LazySingleton.instance;
}

public showMessage(): void {
console.log("Hello from LazySingleton!");
}
}

// 使用
const lazy1 = LazySingleton.getInstance(); // 第一次调用时创建
const lazy2 = LazySingleton.getInstance();
console.log(lazy1 === lazy2); // true

唯一的区别就是懒汉和饱汉,懒汉就是在调用的时候才生成,容易造成多线程问题,饱汉就是直接初始化要用的时候直接返回,天生的线程安全,但资源浪费