Skip to content

Commit a46f5b6

Browse files
yunnayunna
yunna
authored and
yunna
committed
工厂模式
1 parent c629cbf commit a46f5b6

File tree

13 files changed

+173
-41
lines changed

13 files changed

+173
-41
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@
3232

3333
## 设计模式研究
3434

35-
36-
- [1、工厂模式](https://github.com/SunshineBrother/JHBlog/blob/master/设计模式/1、工厂模式/工厂模式.md)
37-
- [2、策略模式](https://github.com/SunshineBrother/JHBlog/blob/master/设计模式/2、策略模式/策略模式.md)
35+
- [1、UML了解](https://github.com/SunshineBrother/JHBlog/blob/master/设计模式/UML/UML类图几种关系的总结.md)
36+
- [2、工厂模式](https://github.com/SunshineBrother/JHBlog/blob/master/设计模式/1、工厂模式/工厂模式.md)
37+
- [3、策略模式](https://github.com/SunshineBrother/JHBlog/blob/master/设计模式/2、策略模式/策略模式.md)
3838

3939

4040

设计模式/1、工厂模式/demo/demo.xcodeproj/project.pbxproj

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@
1414
28120CD0212D0ABB0053FA3F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 28120CCE212D0ABB0053FA3F /* LaunchScreen.storyboard */; };
1515
28120CDB212D0ABB0053FA3F /* demoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28120CDA212D0ABB0053FA3F /* demoTests.swift */; };
1616
28120CE6212D0ABB0053FA3F /* demoUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28120CE5212D0ABB0053FA3F /* demoUITests.swift */; };
17-
28120CF4212D0CE10053FA3F /* BaseCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28120CF3212D0CE10053FA3F /* BaseCell.swift */; };
1817
28120CF6212D0DAF0053FA3F /* Model.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28120CF5212D0DAF0053FA3F /* Model.swift */; };
19-
28120CF8212D0ED50053FA3F /* OneBaseCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28120CF7212D0ED50053FA3F /* OneBaseCell.swift */; };
2018
28120CFA212D0EE00053FA3F /* TwoBaseCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28120CF9212D0EE00053FA3F /* TwoBaseCell.swift */; };
2119
28120CFC212D0EF00053FA3F /* ThreeBaseCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28120CFB212D0EF00053FA3F /* ThreeBaseCell.swift */; };
20+
28250869212FB7CD003E7AB5 /* simpleFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28250868212FB7CD003E7AB5 /* simpleFactory.swift */; };
21+
2825086B212FBAD8003E7AB5 /* OneBaseCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2825086A212FBAD8003E7AB5 /* OneBaseCell.swift */; };
22+
2825086D212FD65A003E7AB5 /* FactoryCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2825086C212FD65A003E7AB5 /* FactoryCell.swift */; };
2223
/* End PBXBuildFile section */
2324

2425
/* Begin PBXContainerItemProxy section */
@@ -52,11 +53,12 @@
5253
28120CE1212D0ABB0053FA3F /* demoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = demoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
5354
28120CE5212D0ABB0053FA3F /* demoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = demoUITests.swift; sourceTree = "<group>"; };
5455
28120CE7212D0ABB0053FA3F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
55-
28120CF3212D0CE10053FA3F /* BaseCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseCell.swift; sourceTree = "<group>"; };
5656
28120CF5212D0DAF0053FA3F /* Model.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Model.swift; sourceTree = "<group>"; };
57-
28120CF7212D0ED50053FA3F /* OneBaseCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OneBaseCell.swift; sourceTree = "<group>"; };
5857
28120CF9212D0EE00053FA3F /* TwoBaseCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TwoBaseCell.swift; sourceTree = "<group>"; };
5958
28120CFB212D0EF00053FA3F /* ThreeBaseCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreeBaseCell.swift; sourceTree = "<group>"; };
59+
28250868212FB7CD003E7AB5 /* simpleFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = simpleFactory.swift; sourceTree = "<group>"; };
60+
2825086A212FBAD8003E7AB5 /* OneBaseCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OneBaseCell.swift; sourceTree = "<group>"; };
61+
2825086C212FD65A003E7AB5 /* FactoryCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FactoryCell.swift; sourceTree = "<group>"; };
6062
/* End PBXFileReference section */
6163

6264
/* Begin PBXFrameworksBuildPhase section */
@@ -110,10 +112,11 @@
110112
28120CC5212D0AB90053FA3F /* AppDelegate.swift */,
111113
28120CC7212D0AB90053FA3F /* ViewController.swift */,
112114
28120CF5212D0DAF0053FA3F /* Model.swift */,
113-
28120CF3212D0CE10053FA3F /* BaseCell.swift */,
114-
28120CF7212D0ED50053FA3F /* OneBaseCell.swift */,
115+
2825086A212FBAD8003E7AB5 /* OneBaseCell.swift */,
115116
28120CF9212D0EE00053FA3F /* TwoBaseCell.swift */,
116117
28120CFB212D0EF00053FA3F /* ThreeBaseCell.swift */,
118+
28250868212FB7CD003E7AB5 /* simpleFactory.swift */,
119+
2825086C212FD65A003E7AB5 /* FactoryCell.swift */,
117120
28120CC9212D0AB90053FA3F /* Main.storyboard */,
118121
28120CCC212D0ABB0053FA3F /* Assets.xcassets */,
119122
28120CCE212D0ABB0053FA3F /* LaunchScreen.storyboard */,
@@ -271,10 +274,11 @@
271274
isa = PBXSourcesBuildPhase;
272275
buildActionMask = 2147483647;
273276
files = (
277+
28250869212FB7CD003E7AB5 /* simpleFactory.swift in Sources */,
278+
2825086B212FBAD8003E7AB5 /* OneBaseCell.swift in Sources */,
274279
28120CC8212D0AB90053FA3F /* ViewController.swift in Sources */,
275-
28120CF4212D0CE10053FA3F /* BaseCell.swift in Sources */,
276-
28120CF8212D0ED50053FA3F /* OneBaseCell.swift in Sources */,
277280
28120CFA212D0EE00053FA3F /* TwoBaseCell.swift in Sources */,
281+
2825086D212FD65A003E7AB5 /* FactoryCell.swift in Sources */,
278282
28120CFC212D0EF00053FA3F /* ThreeBaseCell.swift in Sources */,
279283
28120CC6212D0AB90053FA3F /* AppDelegate.swift in Sources */,
280284
28120CF6212D0DAF0053FA3F /* Model.swift in Sources */,
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//
2+
// FactoryCell.swift
3+
// demo
4+
//
5+
// Created by yunna on 2018/8/24.
6+
// Copyright © 2018年 yunna. All rights reserved.
7+
//
8+
9+
10+
import UIKit
11+
12+
class FactoryCell: UITableViewCell {
13+
func configUI(model: Model, tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
14+
15+
return UITableViewCell()
16+
}
17+
}

设计模式/1、工厂模式/demo/demo/OneBaseCell.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import UIKit
1010

11-
class OneBaseCell: UITableViewCell {
11+
class OneBaseCell: FactoryCell {
1212

1313
var IV = UIImageView()
1414
var label = UILabel()
@@ -27,10 +27,19 @@ class OneBaseCell: UITableViewCell {
2727
fatalError("init(coder:) has not been implemented")
2828
}
2929

30+
31+
32+
override func configUI(model: Model, tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
33+
let cell = tableView.dequeueReusableCell(withIdentifier: model.reuseId!, for: indexPath)
34+
self.configCell(model)
35+
return cell
36+
}
37+
3038
func configCell(_ model: Model) {
3139
self.IV.image = UIImage.init(named: model.imagePath)
3240
self.label.text = model.title
3341
self.backgroundColor = UIColor.gray
3442
}
3543

44+
3645
}

设计模式/1、工厂模式/demo/demo/ThreeBaseCell.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,15 @@
88

99
import UIKit
1010

11-
class ThreeBaseCell: UITableViewCell {
11+
class ThreeBaseCell: FactoryCell {
1212

13+
14+
override func configUI(model: Model, tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
15+
let cell = tableView.dequeueReusableCell(withIdentifier: model.reuseId!, for: indexPath)
16+
self.configCell(model)
17+
return cell
18+
}
19+
1320
func configCell(_ model: Model) {
1421
self.textLabel?.text = model.title
1522
self.backgroundColor = UIColor.red

设计模式/1、工厂模式/demo/demo/TwoBaseCell.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import UIKit
1010

11-
class TwoBaseCell: UITableViewCell {
11+
class TwoBaseCell: FactoryCell {
1212

1313
var IV = UIImageView()
1414

@@ -22,6 +22,12 @@ class TwoBaseCell: UITableViewCell {
2222
fatalError("init(coder:) has not been implemented")
2323
}
2424

25+
override func configUI(model: Model, tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
26+
let cell = tableView.dequeueReusableCell(withIdentifier: model.reuseId!, for: indexPath)
27+
self.configCell(model)
28+
return cell
29+
}
30+
2531
func configCell(_ model: Model) {
2632
self.IV.image = UIImage.init(named: model.imagePath)
2733
self.backgroundColor = UIColor.yellow

设计模式/1、工厂模式/demo/demo/ViewController.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ extension ViewController: UITableViewDelegate, UITableViewDataSource {
5050
}
5151

5252
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
53-
let cell = BaseCell().configUI(model: self.dataArr[indexPath.row], tableView: tableView, indexPath: indexPath)
53+
let cell = simpleFactory().configUI(model: self.dataArr[indexPath.row], tableView: tableView, indexPath: indexPath)
54+
5455
return cell
5556
}
5657
}
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,14 @@
11
//
2-
// BaseCell.swift
2+
// simpleFactory.swift
33
// demo
44
//
5-
// Created by yunna on 2018/8/22.
5+
// Created by yunna on 2018/8/24.
66
// Copyright © 2018年 yunna. All rights reserved.
77
//
88

99
import UIKit
1010

11-
class BaseCell: UITableViewCell {
12-
13-
override func awakeFromNib() {
14-
super.awakeFromNib()
15-
// Initialization code
16-
}
17-
18-
override func setSelected(_ selected: Bool, animated: Bool) {
19-
super.setSelected(selected, animated: animated)
20-
21-
// Configure the view for the selected state
22-
}
23-
11+
class simpleFactory: NSObject {
2412
func configUI(model: Model, tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
2513
print(model.reuseId)
2614
let cell = tableView.dequeueReusableCell(withIdentifier: model.reuseId!, for: indexPath)
@@ -36,4 +24,6 @@ class BaseCell: UITableViewCell {
3624
}
3725

3826

27+
28+
3929
}
12.1 KB
Loading
12.2 KB
Loading

设计模式/1、工厂模式/工厂模式.md

Lines changed: 109 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121

2222
### 案例
2323
我们大概了解了什么是工厂模式,那么我们就要该在项目中运用一下了。
24+
`这里我们把调用方称呼为客户端`
25+
26+
**普通模式**
2427
我们常见的UITableView列表包含了各种不同的UI展示的Cell。我们在其相关的代理方法实现可能是这样的:
2528

2629
```
@@ -47,21 +50,21 @@
4750
4851
```
4952

50-
随着需求的迭代,我们后期可能会增加更多样式的`Cell`,此时我们就需要变更这部分的代码。增加与之对应的`else if`条件判断,代码将变得越来越长。同时,在更加复杂的场景下,我们并不能保证调整原有代码以后,之前功能不受影响。我们将投入更多的时间进行原有代码功能的回归,这是我们不希望发生的。
53+
上面这样写会有以下几个问题
54+
- 1、随着需求的迭代,我们后期可能会增加更多样式的`Cell`,此时我们就需要变更这部分的代码。增加与之对应的`else if`条件判断,代码将变得越来越长。同时,在更加复杂的场景下,我们并不能保证调整原有代码以后,之前功能不受影响。我们将投入更多的时间进行原有代码功能的回归,这是我们不希望发生的。
5155

56+
- 2、客户端必须知道所有`cell`类对象,耦合度太高。客户端必须知道所有具体的`cell`类,那么任何一个类的改动都可能会影响到客户端。
5257

53-
1.将各种展示功能相似的Cell抽象为`BaseCell`(继承于UITableViewCell),`BaseCell`提供默认展示UI的方法`configUI`
54-
2.其他多种的Cell根据其特定的需求对`configUI`方法进覆写
58+
**简单工厂**
59+
`客户端`必须了解所有的具体`cell`类才能创建对象,但是这会导致上述一系列问题。那么解决办法就是`把这些对象的创建封装起来,对客户端不可见`,那么之后如何改动具体类都不会影响到客户端。这可以通过简单工厂来实现。
60+
61+
62+
简单工厂代码
5563

56-
这时我们的代理方法就变成了这些
57-
```
58-
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
59-
let cell = BaseCell().configUI(model: self.dataArr[indexPath.row], tableView: tableView, indexPath: indexPath)
60-
return cell
61-
}
62-
```
63-
我们在BaseCell这个工厂里面写各种需求
6464
```
65+
import UIKit
66+
67+
class simpleFactory: NSObject {
6568
func configUI(model: Model, tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
6669
print(model.reuseId)
6770
let cell = tableView.dequeueReusableCell(withIdentifier: model.reuseId!, for: indexPath)
@@ -73,9 +76,104 @@
7376
(cell as! ThreeBaseCell).configCell(model)
7477
}
7578
79+
return cell
80+
}
81+
82+
}
83+
```
84+
客户端调用代码:
85+
```
86+
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
87+
let cell = simpleFactory().configUI(model: self.dataArr[indexPath.row], tableView: tableView, indexPath: indexPath)
88+
return cell
89+
}
90+
```
91+
此时不管是增加还是减少或者修改`cell`类型,客户端代码都不用改动,降低了客户端和具体`cell`类的耦合,也遵循了开闭原则
92+
93+
94+
前面我们把创建具体`cell`对象的代码放在客户端,导致一系列问题。我们的目标就是让客户端从创建具体对象中解耦出来,让客户端不知道对象创建的具体过程。而简单工厂就是和具体对象封装在一起,算是一个封装体内,所以简单工厂知道具体的实现类是没有关系的。现在客户端只要知道简单工厂和一个抽象类`cell`,就可以创建具体对象了,实现了解耦。
95+
96+
**改进**
97+
98+
虽然上面使用简单工厂后,让客户端实现了解耦,但是如果实现类改变了,我们还是需要需改简单工厂。有没有什么办法做到即使实现类改变也不需要改变简单工厂的代码呢?
99+
100+
这时我们可以使用`NSClassFromString`这个方法,根据`类名`生成相应的类
101+
```
102+
func configUI(model: Model, tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
103+
104+
let nameSpace = Bundle.main.infoDictionary?["CFBundleName"] as? String ?? ""
105+
guard let cls = NSClassFromString(nameSpace + "." + model.reuseId) as? UITableViewCell.Type else {
106+
return UITableViewCell()
107+
}
108+
// 通过得到的class类型创建对象
109+
let cellClass = cls.init()
110+
111+
return cellClass
112+
}
113+
```
114+
`其实使用这个方法是不准确的,因为这样我们不能从外边传入值,只有那些不需要外边传值的情况才可以`
115+
116+
**最终版本**
117+
118+
创建一个`FactoryCell`的工厂类,然后其他的cell继承这个`FactoryCell`,然后`FactoryCell`工厂类里面创建一个方法
119+
```
120+
import UIKit
121+
122+
class FactoryCell: UITableViewCell {
123+
func configUI(model: Model, tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
124+
125+
return UITableViewCell()
126+
}
127+
}
128+
129+
```
130+
其他子类重写`func configUI(model: Model, tableView: UITableView, indexPath: IndexPath)`方法就行了
131+
132+
子类
133+
```
134+
import UIKit
135+
136+
class OneBaseCell: FactoryCell {
137+
override func configUI(model: Model, tableView: UITableView, indexPath: IndexPath) -> UITableViewCell {
138+
let cell = tableView.dequeueReusableCell(withIdentifier: model.reuseId!, for: indexPath)
139+
self.configCell(model)
140+
return cell
141+
}
142+
143+
func configCell(_ model: Model) {
144+
self.IV.image = UIImage.init(named: model.imagePath)
145+
self.label.text = model.title
146+
self.backgroundColor = UIColor.gray
147+
}
148+
149+
}
150+
```
151+
152+
而在UITableViewDataSource的代理方法中我们只需要这样
153+
```
154+
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
155+
let cell = simpleFactory().configUI(model: self.dataArr[indexPath.row], tableView: tableView, indexPath: indexPath)
156+
76157
return cell
77158
}
78159
```
160+
我们即使是一直不停的添加不同的`cell`,我们也不需要更改什么代码了,只需要新的`cell`重写`func configUI(model: Model, tableView: UITableView, indexPath: IndexPath)`方法就行了
161+
162+
163+
164+
165+
166+
### 我们通过流程图看看这样写的效果
167+
168+
![一般思路](https://github.com/SunshineBrother/JHBlog/blob/master/设计模式/1、工厂模式/一般思路.png)
169+
170+
![工厂方法](https://github.com/SunshineBrother/JHBlog/blob/master/设计模式/1、工厂模式/工厂方法.png)
171+
172+
173+
174+
一般的思路,客户端完全依赖于具体的低层组件`cell`
175+
工厂方法 客户端依赖于工厂cell,各个具体的`cell`继承与`FactoryCell`,随便添加cell,对其他部分代码完全没有影响
176+
79177

80178

81179
[参考demo](https://github.com/SunshineBrother/JHBlog/tree/master/设计模式/1、工厂模式)

0 commit comments

Comments
 (0)