Java中那些巧妙的设计

本文是本人从事开发中学习到的一些Java知识,在这里共享给大家

Builder模式

我们都知道新建一个对象一般都是通过其构造方式产生,但是如果我们的类具有多个属性,我们就可能需要生成多个构造方法,去实现不同的需求。但是这样的方式明显是非常繁琐的。所以builder模式很好的解决了这一问题。builder模式根据不同的需求可以随意更改任意属性。(在Android开发中Dialog就是采用的这种方式,灵活的更改属性值,下面我们就以它为例)

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
public class AndroidDialog {
private String title;
private String message;
private OnClickListener listener;

public AndroidDialog(String title, String message) {
this.title = title;
this.message = message;
}

public AndroidDialog(String title, String message, OnClickListener listener) {
this.title = title;
this.message = message;
this.listener = listener;
}

public AndroidDialog(String message, OnClickListener listener) {
this.message = message;
this.listener = listener;
}

public interface OnClickListener{
void onClick();
}
}
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
public class AndroidDialog {
private String title;
private String message;
private OnClickListener listener;

public static class Builder {
private String title;
private String message;
private OnClickListener listener;

//这里也可以一样传值
public Builder(){

}

public Builder setMessage(String message){
this.message=message;
return this;
}

public Builder setTitle(String title){
this.title=title;
return this;
}

public Builder setButtonListener(OnClickListener listener){
this.listener=listener;
return this;
}

//最后通过build生产

public AndroidDialog build(){
AndroidDialog androidDialog=new AndroidDialog();
androidDialog.title=title;
androidDialog.message=message;
androidDialog.listener=listener;
return androidDialog;
}
}

public interface OnClickListener{
void onClick();
}
}

通过上面两种方式的比较,我们可以明显的看出区别在哪儿,builder更适合那种参数多,自定义性强的类。下面是使用方法:

1
2
3
4
5
6
7
8
9
AndroidDialog androidDialog = new AndroidDialog.Builder()
.setTitle("标题")
.setMessage("message")
.setButtonListener(new AndroidDialog.OnClickListener() {
@Override
public void onClick() {

}
}).build();

getInstance()单例模式

单例模式,在我们的日常开发中当然随处可见。但是各种单例模式方式的实现各有不同。先简单介绍下单例模式的功能

序号 特点
1 构造函数不对外开放,一般为private
2 单例类必须自己创建自己的唯一实例
3 单例类必须给所有其他对象提供这一实例

实现单例模式的方式有很多。 我们来以逼格的等级来看看这里方式。

菜逼(简单暴力)

1
2
3
4
5
6
7
8
9
public class Person {
private static Person instance;
private Person() {}
public static Person getInstance() {
if(instance == null)
instance = new Person();
return instance;
}
}

这种方式简单容易理解,但是缺点是线程是非安全的。比如线程同时执行getInstance(),可能会同时创建出两个不同的对象。所以尽量在方法外面加上synchronized
当然你也可以使用该变量之前就将该变量进行初始化,这个是线程安全的,写法也很简单。

1
2
3
4
5
6
7
public class Person {
private static Person instance=new Person();
private Person() {}
public static Person getInstance() {
return instance;
}
}

在程序启动时Person就会创建你的static instance,消耗资源(优点:启动快)。不推荐。

==注意我要开始装逼了==

刚刚提到使用synchronized来加同步锁。看起来不错了,但是每次调用getInstance()都需要synchronized,似乎还可以改进。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Person {
private static Person instance;
private Person() {}

public static Person getInstance() {
if(instance == null) {
synchronized(Person.class) {
instance = new Person();
}
}
return instance;
}

}

看起来似乎不错,但是却存在着大BUG。 如果两个线程同时进入null判断。但是还没有进入synchronized,那么这个时候肯定会排着队产生两个Person。继续改善:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Person {
private static Person instance;
private Person() {}

public static Person getInstance() {
if(instance == null) {
synchronized(Object.class) {
if(instance == null) {
instance = new Person();
}
}
}
return instance;
}

}

进入synchronized再做判断,这样就可以避免同时产生两个对象了。是的吗? 好像还是不行。因为还有种可能线程1还在 new Person()的过程中,这个时候线程2冲进来。这下就会返回一个没完全初始化好的Peson,程序必然报错。错误详情可参见 http://www.race604.com/java-double-checked-singleton/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io

==看来装逼还得抓把劲==

这个时候我们需要引用volatile(自行百度,大致就是我没初始化完成,别人就拿不到这个对象)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Person {
private static volatile Person instance;
private Person() {}

public static Person getInstance() {
if(instance == null) {
synchronized(Object.class) {
if(instance == null) {
instance = new Person();
}
}
}
return instance;
}
}

擦,这个逼终于装完了。

还没完, 据说这才是最高境界的装逼(哦 不,单例)。

1
2
3
4
5
6
7
8
9
public enum  Person {
INSTANCE;
private Person(){

}
public Person getInstance(){
return INSTANCE;
}
}

下次再有谁和你提单例,直接甩他一脸枚举。
是不是已经学会装逼(单例)了?

Adapter模式

对于Android开发者来说一定离不开各种Adapter。那么这些adapter是怎么来设计的呢?下面一起来探索一下。
首先我们来了解下Adapter是一种什么模式,一般的来解释就是,将一种无法直接使用的东西,通过Adapter来转换成我们需要的东西。(Android中将Model数据转换成视图),还有一种功能就是通过抛出统一的接口实现通用适配。(Android中listview,gridview都可以通过BaseAdapter提供数据)

1
2
3
4
ListView->Adapter: RecycleBin
Adapter-->ListView: ContentView
Model->Adapter: ViewHolder
Adapter-->Model: getView

Android在继承BaseAdapter中强制实现了,getCount,getView方法。我们知道这两个方法是必须填返回值的,否则视图无法展现。那么ListViewsetAdapter后做了哪些操作使得Model数据能够展示在View上呢? 我们自己简单的新建一个横向ListView。 上车了。滴滴

1
2
3
4
5
6
7
8
9
10
11
12
public class HorizontalView extends HorizontalScrollView{
private static final String TAG = "HorizontalView";
LinearLayout listview;
BaseAdapter baseAdapter;
public HorizontalView(Context context) {
super(context);
}

public void setAdapter(BaseAdapter baseAdapter){

}
}

注意:这里的BaseAdapter是我们自定义的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 作者 by yugai 时间 16/6/16.
* * 邮箱 784787081@qq.com
*/

public abstract class BaseAdapter {
/**
* 获取填充数组的size
* @return 长度
*/

abstract int getCount();

/**
* 获取返回的视图
* @param position 当前项index
* @return ContentView
*/

abstract View getView(int position);
}

这里我们只是为了了解基础的使用方法,所以就简单的定义了两个方法。下面该怎么做应该都清楚了。 只需继承BaseAdapter实现其方法,就能够将我们的model数组根据下标设置到HorizontalView上了。 不做解释了,上车

1
2
3
4
5
6
7
8
9
10
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
HorizontalView horizontalView=new HorizontalView(this);
List<String> datas =new ArrayList<>();
for (int i = 0; i < 10; i++) {
datas.add(""+i);
}
horizontalView.setAdapter(new TestAdapter(this,datas));
setContentView(horizontalView);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class TestAdapter extends BaseAdapter{
Context mContext;
List<String> datas;
public TestAdapter(Context context,List<String> datas){
this.mContext=context;
this.datas=datas;
}

@Override
int getCount() {
return datas.size();
}

@Override
View getView(int position) {
View convertView= LayoutInflater.from(mContext).inflate(android.R.layout.simple_expandable_list_item_1,null);
((TextView)convertView.findViewById(android.R.id.text1)).setText("--------"+datas.get(position)+"--------");
return convertView;
}
}

大家应该能看懂吧。这个ListView就是这么设置Adapter的。这样我们在Adapter中将model数据绑定到视图上了。那么Adapter的转换功能就顺利完成了。下面就是for循环将视图绑定到ListView上了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class HorizontalView extends HorizontalScrollView{
private static final String TAG = "HorizontalView";
LinearLayout listview;
BaseAdapter baseAdapter;
public HorizontalView(Context context) {
super(context);
listview=new LinearLayout(context);
}

public void setAdapter(BaseAdapter baseAdapter){
this.baseAdapter=baseAdapter;
fillView();
}

private void fillView() {
for (int i = 0; i < baseAdapter.getCount(); i++) {
listview.addView(baseAdapter.getView(i));
}
addView(listview);
}
}

好吧。至此为止我们的横向listView已经打造好了。在过程中可以学习到adapter的大概工作模式(当然listView的数据绑定远远没有这么简单)。