Java方法参数过多 - 吴明
- 示例方法
1 | public void getNews(Context context, |
- 示例构造函数
1 | public class Person { |
- 问题:
- 添加大量参数理解难度
- 易参数位置不正确且运行正常
- 不易维护
- 那一个方法或者构造方法多少个参数才好了—没有答案
- Robert Martin在Clean Code写到
- Steve McConnell在Code Complete中写到:开发者应该限制参数在七个以内
1 | 函数参数的理想个数是零,其次是一,紧随其后的是二,应该尽可能避免三个参数的情况。参数如果多于三个则需要特殊的理由,而且无论如何都不应该再使用。 |
解决方法
引入参数对象
参数关系紧密合并到一个对象中
Person.class
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class Person {
public FullName fullName;
public Address address;
public boolean isFemale;
public boolean isEmployed;
public boolean isHomeOwner;
public Person(FullName fullName, Address address, boolean isFemale, boolean isEmployed, boolean isHomeOwner) {
this.fullName = fullName;
this.address = address;
this.isFemale = isFemale;
this.isEmployed = isEmployed;
this.isHomeOwner = isHomeOwner;
}
}
- FullName.class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class FullName {
public String lastName;
public String firstName;
public String middleName;
public String salutation;
public String suffix;
public FullName(String lastName, String firstName, String middleName, String salutation, String suffix) {
this.lastName = lastName;
this.firstName = firstName;
this.middleName = middleName;
this.salutation = salutation;
this.suffix = suffix;
}
}
- Address.class
1
2
3
4
5
6
7
8
9
10
public class Address {
public String streetAddress;
public String city;
public String state;
public Address(String streetAddress, String city, String state) {
this.streetAddress = streetAddress;
this.city = city;
this.state = state;
}
}
- 问题:参数对象可能被滥用。如果一个开发者纯粹为了减少参数数量,把联系不紧的几个参数强捆在一个类中这肯定是行不通的,在可读性上甚至适得其反。
Builder模式:
- 需求:当一个对象需要不同的参数构造方法?不能写5*5满足所有的需求吧
- 适用范围:构建对象时,如果碰到类有很多参数——其中很多参数类型相同而且很多参数可以为空时,使用Builder模式来完成。当参数数量不多、类型不同而且都是必须出现时,通过增加代码实现Builder往往无法体现它的优势。在这种情况下,理想的方法是调用传统的构造函数。再者,如果不需要保持不变,那么就使用无参构造函数调用相应的set方法吧。
代码
- Person.class
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
58public class Person {
public FullName fullName;
public Address address;
public boolean isFemale;
public boolean isEmployed;
public boolean isHomeOwner;
public Person(FullName fullName, Address address, boolean isFemale, boolean isEmployed, boolean isHomeOwner) {
this.fullName = fullName;
this.address = address;
this.isFemale = isFemale;
this.isEmployed = isEmployed;
this.isHomeOwner = isHomeOwner;
}
public static class Builder {
private FullName fullName;
private Address address;
private boolean isFemale;
private boolean isEmployed;
private boolean isHomeOwner;
/**
* 如果有必填参数这里可以构造必填构造方法
*/
public Builder() {
}
public Builder setFullName(FullName fullName) {
this.fullName = fullName;
return this;
}
public Builder setAddress(Address address) {
this.address = address;
return this;
}
public Builder setFemale(boolean female) {
isFemale = female;
return this;
}
public Builder setEmployed(boolean employed) {
isEmployed = employed;
return this;
}
public Builder setHomeOwner(boolean homeOwner) {
isHomeOwner = homeOwner;
return this;
}
public Person create() {
return new Person(fullName, address, isFemale, isEmployed, isHomeOwner);
}
}
}
- FullName.class
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
public class FullName {
public String lastName;
public String firstName;
public String middleName;
public String salutation;
public String suffix;
public FullName(String lastName, String firstName, String middleName, String salutation, String suffix) {
this.lastName = lastName;
this.firstName = firstName;
this.middleName = middleName;
this.salutation = salutation;
this.suffix = suffix;
}
public static class Builder {
private String lastName;
private String firstName;
private String middleName;
private String salutation;
private String suffix;
public Builder() {
}
public Builder setLastName(String lastName) {
this.lastName = lastName;
return this;
}
public Builder setFirstName(String firstName) {
this.firstName = firstName;
return this;
}
public Builder setMiddleName(String middleName) {
this.middleName = middleName;
return this;
}
public Builder setSalutation(String salutation) {
this.salutation = salutation;
return this;
}
public Builder setSuffix(String suffix) {
this.suffix = suffix;
return this;
}
public FullName create() {
return new FullName(lastName, firstName, middleName, salutation, suffix);
}
}
}
- Address.class
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
public class Address {
public String streetAddress;
public String city;
public String state;
public Address(String streetAddress, String city, String state) {
this.streetAddress = streetAddress;
this.city = city;
this.state = state;
}
public static class Builder {
private String streetAddress;
private String city;
private String state;
public Builder() {
}
public Builder setStreetAddress(String streetAddress) {
this.streetAddress = streetAddress;
return this;
}
public Builder setCity(String city) {
this.city = city;
return this;
}
public Builder setState(String state) {
this.state = state;
return this;
}
public Address create() {
return new Address(streetAddress, city, state);
}
}
}
调用的地方
1
2
3
4
5
6
7
8
public static void main(String[] args) {
FullName fullName = new FullName.Builder().setFirstName("yes")
.setLastName("no").create();
Address address = new Address.Builder().setCity("china").setState("12")
.create();
Person person = new Person.Builder().setAddress(address)
.setFullName(fullName).create();
}
- 优点:客户端代码的可用性和可读性得到了大大提高,构造函数的参数数量明显减少调用起来非常直观。单个builder构建多个对象时Builder参数可在创建期间进行调整,还可以根据对象不同而进行改变,有效的避免重载构造函数。
- 缺点:增加代码量,代码变得更加冗长(相比较参数数量的增加,相同类型的参数混在一起,可选参数的增加而言,改善代码可读性更有价值)
重载
- 适用范围:方法中参数可选参数或者参数中指定参数相同
代码
1
2
3
4
5
6
7
8
9
10
11public String name(String name,int year) {
return name+year;
}
/**
* 重载
* @param name
* @return
*/
public String name(String name) {
return name+"null";
}
- 优点:遇到可选参数或者默认参数时,使用方法重载会十分有效。
使用工具
- 静态分析工具
- android—Android studio
- 其他Java静态代码分析工具
- 静态分析工具
Swift与JavaScript交互 - 刘康
概述
iOS原生应用和web页面的交互大致上有这几种方法:
- iOS7之后的
JavaScriptCore
拦截协议
- 第三方框架
WebViewJavaScriptBridge
: 是基于拦截协议进行的封装,学习成本相对JavaScriptCore较高 - iOS8之后的
WKWebView
: iOS8之后推出的,还没有成为主流使用。
关于JavaScriptCore
涉及到的几种类型:
- JSContext: JSContext是代表JS的执行环境,通过-evaluateScript:方法就可以执行JS代码
- JSValue: JSValue封装了JS与ObjC中的对应的类型,以及调用JS的API等
- JSExport: JSExport是一个协议,遵守此协议,就可以定义我们自己的协议,在协议中声明的API都会在JS中暴露出来,才能调用
Swift与JS交互方式
两种调用JS代码的方法:
1、直接调用JS代码
2、在Swift中通过JSContext注入模型,然后调用模型的方法
直接调用JS代码
我们可以不通过模型来调用方法,也可以直接调用方法
1 | // 直接JS方法 |
这种方式是没有注入模型到JS中的。这种方式使用起来不太合适,通常在JS中有很多全局的函数,为了防止名字重名,使用模型的方式是最好不过了。通过我们协商好的模型名称,在JS中直接通过模型来调用我们在Swift中所定义的模型所公开的API。
注入模型的交互
注入模型的交互
首先,我们需要先定义一个协议,而且这个协议必须要遵守JSExport协议。
All methods that should apply in Javascript,should be in the following protocol.
注意,这里必须使用@objc
,因为JavaScriptCore库是ObjectiveC版本的。如果不加@objc
,则调用无效果。
1 | objc protocol JavaScriptSwiftDelegate: JSExport { |
接下来,我们还需要定义一个模型:
1 | @objc class SwiftJSModel: NSObject, JavaScriptSwiftDelegate { |
接下来,我们在controller中在webview加载完成的代理中,给JS注入模型:
1 | func webViewDidFinishLoad(webView: UIWebView) { |
JSContext是通过webView的valueForKeyPath获取的,其路径为documentView.webView.mainFrame.javaScriptContext。
这样就可以获取到JS的context,然后为这个context注入模型对象。
先写两个JS方法:
1 | function jsFunc() { |
这里定义了两个JS方法,一个是jsFunc,不带参数。
另一个是jsParamFunc,带一个参数。
当点击第一个按钮:Call ObjC system camera时,
通过OCModel.callSystemCamera(),就可以在HTML中通过JS调用OC的方法。
在Swift代码callSystemCamera方法体中,添加了以下两行代码,就是获取HTML中所定义的JS就去jsFunc,然后调用它。
1 | let jsFunc = self.jsContext?.objectForKeyedSubscript("jsFunc"); |
这样就可以在JS调用Siwft方法时,也让Swift反馈给JS。
注意:这里是通过objectForKeyedSubscript方法来获取变量jsFunc。
方法也是变量。看看下面传字典参数:
1 | func jsCallObjcAndObjcCallJsWithDict(dict: [String : AnyObject]) { |
获取HTML中定义的jsParamFunc方法,然后调用它并传了一个字典作为参数。