的特征之一,所以我们需要将字段变成私有的,然后对外提供相应的setter和getter方法。具体做法如下所示。
1 //重构前
2 class Person {
3 var name: String = ""
4
5 init(name: String) {
6 self.name = name
7 }
8 }
9
10 //重构后
11 class Person {
12 private var name: String = ""
13
14 init(name: String) {
15 self.name = name
16 }
17
18 func getName() -> String {
19 return name
20 }
21
22 func setName(name: String) {
23 self.name = "China:" + name
24 }
25 }
十、Encapsulate Collection(封装集合)
“封装集合”这一重构规则应该来说并不难理解。当你的类中有集合时,为了对该集合进行封装,你需要为集合创建相应的操作方法,例如增删改查等等。下方就通过一个不封装集合的实例,看一下缺点。然后将其重构。关于“封装集合”具体的细节参见下方实例。
1.未封装集合的实例
下方我们先创建一个图书馆图书类,为了简化示例,该图书类只有一个书名。下方代码段就是这个图书类,如下所示:
class LibraryBook {
private var name: String
init(name: String) {
self.name = name
}
func getName() -> String {
return self.name
}
}
紧接着要创建一个借书者,借书者中有两个字段,一个是借书者的名字,另一个是所借书籍的数组。在Lender中我们没有为lendBooks数组封装相应的方法,只为其提供了getter/setter方法,具体代码如下所示。
1 class Lender {
2 private var name: String
3 private var lendBooks: Array<LibraryBook> = []
4
5 init(name: String) {
6 self.name = name
7 }
8
9 func getName() -> String {
10 return self.name
11 }
12
13 func setLendBooks(books: Array<LibraryBook>) {
14 self.lendBooks = books
15 }
16
17 func getLendBooks() -> Array<LibraryBook> {
18 return self.lendBooks
19 }
20 }
紧接着我们要创建一个测试用例,观察这两个类的使用方式。由下面程序的注释可知,首先我们需要创建一个books的数组,该数组就像一个篮子似的,它可以存储我们要借的书籍。让后将创建的书籍添加到该数组中,最后将books赋值给借书人中的lendBooks。如果要对书籍进行修改,那么只有先获取借书人的lendBooks, 然后进行修改,最后再将修改后的值赋值回去。
1 //先创建一个书籍数组
2 var books: Array<LibraryBook> = []
3 //添加要借的书籍
4 books.append(LibraryBook(name: "《雪碧加盐》"))
5 books.append(LibraryBook(name: "《格林童话》"))
6 books.append(LibraryBook(name: "《智慧意林》"))
7
8 //创建借书人
9 let lender: Lender = Lender(name: "ZeluLi")
10 lender.setLendBooks(books)
11
12 //获取所借书籍
13 var myBooks = lender.getLendBooks()
14
15 //对书籍数组修改后再赋值回去
16 myBooks.removeFirst()
17 lender.setLendBooks(myBooks)
2.为上面的Lender类添加相应的集合操作的方法
由上面的测试用例可以看出,Lender类封装的不好。因为其使用方式以及调用流程太麻烦,所以我们得重新对其进行封装。所以就会用到“Encapsulate Collection”原则。下面我们就会为Lender添加上相应的集合操作的方法。说白了,就是讲上面测试用例做的一部分工作放到Lender类中。下方是为Lender添加的对lendBooks相应的操作方法。下方代码中的Lender类与上面的Lender类中的lendBooks不同,我们使用了另一个集合类型,也就是字典,而字典的key就是书名,字典的值就是书的对象。具体代码如下所示:
经过上面这样一封装的话,使用起来就更为合理与顺手了。用大白话讲,就是好用。下方是我们重新封装后的测试用例,简单了不少,而且组织也更为合理。具体请看下方代码段:
十一、Replace Subclass with Fields(以字段取代子类)
什么叫“以字段取代子类”呢?就是当你的各个子类中唯一的差别只在“返回常量数据”的函数上。当遇到这种情况时,你就可以将这个返回的数据放到父类中,并在父类中创建相应的工厂方法,然后将子类删除即可。直接这样说也许有些抽象,接下来,我们会通过一个小的Demo来看一下这个规则具体如何应用。1.创建多个子类,并每个子类只有一个函数的返回值不同
接下来我们就要创建重构前的代码了。首先我们创建一个PersonType协议(也就是一个抽象类),该协议有两个方法,一个是isMale(),如果是子类是男性就返回true,如果子类是女性就返回false。还有一个是getCode()函数,如果子类是男性就返回“M”,如果是子类是女性就返回“F”。 这两个子类的差别就在于各个函数返回的值不同。下方是PersonType的具体代码。
1 protocol PersonType {
2 func isMale() -> Bool
3 func getCode() -> String
4 }
然后我们基于PersonType创建两个子类,一个是Male表示男性,一个是Female表示女性。具体代码如下:
1 class Male: PersonType {
2 func isMale() -> Bool {
3 return true
4 }
5
6 func getCode() -> String {
7 return SenderCode.Male.rawValue
8 }
9 }
10
11 class Female: PersonTyp