Design Pattern:是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、让代码更容易被他人理解并且保证代码可靠性。
一.为什么要学习设计模式:
1.设计模式来源众多专家的经验和智慧,它们是从许多优秀的软件系统中总结出的成功的、能够实现可维护性、复用的设计方案,使用这些方案将可以让我们避免做一些重复性的工作。
2.设计模式提供了一套通用的设计词汇和一种通用的形式来方便开发人员之间沟通和交流,使得设计方案更加通俗易懂。
3.大部分设计模式都兼顾了系统的可重用性和可扩展性,这使得我们可以更好地重用一些已有的设计方案、功能模块甚至一个完整的软件系统,避免我们经常做一些重复的设计、编写一些重复的代码。
4.合理使用设计模式并对设计模式的使用情况进行文档化,将有助于别人更快地理解系统。
5.学习设计模式将有助于初学者更加深入地理解面向对象思想。
二.六大原则
1.开闭原则:一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。为了满足开闭原则,需要对系统进行抽象化设计,抽象化是开闭原则的关键
2.里氏替换原则:所有引用基类对象的地方能够透明地使用其子类的对象
3.依赖倒置原则:抽象不应该依赖于具体类,具体类应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。
4.单一职责原则:一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。是实现高内聚、低耦合的指导方针。
5.迪米特法则(最少知道原则):一个软件实体应当尽可能少地与其他实体发生相互作用,在类的划分上,应当尽量创建松耦合的类,类之间的耦合度越低,就越有利于复用。在类的结构设计上,每一个类都应当尽量降低其成员变量和成员函数的访问权限。在类的设计上,只要有可能,一个类型应当设计成不变类。在对其他类的引用上,一个对象对其他对象的引用应当降到最低。
6.接口分离原则:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。
三.介绍几种常用的设计模式
1.简单工厂模式
专门定义一个类(工厂类)来负责创建其他类的实例。可以根据创建方法的参数来返回不同类的实例,被创建的实例通常都具有共同的父类。
首先我们看看API提供的Bitmap工厂
public class BitmapFactory {
private static final int DECODE_BUFFER_SIZE = 16 * 1024;
public static Bitmap decodeFile(String pathName, Options opts) {
validate(opts);
Bitmap bm = null;
InputStream stream = null;
try {
stream = new FileInputStream(pathName);
bm = decodeStream(stream, null, opts);
} catch (Exception e) {
/* do nothing.
If the exception happened on open, bm will be null.
*/
Log.e("BitmapFactory", "Unable to decode stream: " + e);
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
// do nothing here
}
}
}
return bm;
}
public static Bitmap decodeFile(String pathName) {
return decodeFile(pathName, null);
}
@Nullable
public static Bitmap decodeResourceStream(@Nullable Resources res, @Nullable TypedValue value,
@Nullable InputStream is, @Nullable Rect pad, @Nullable Options opts) {
validate(opts);
if (opts == null) {
opts = new Options();
}
if (opts.inDensity == 0 && value != null) {
final int density = value.density;
if (density == TypedValue.DENSITY_DEFAULT) {
opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
} else if (density != TypedValue.DENSITY_NONE) {
opts.inDensity = density;
}
}
if (opts.inTargetDensity == 0 && res != null) {
opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
}
return decodeStream(is, pad, opts);
}
public static Bitmap decodeResource(Resources res, int id, Options opts) {
validate(opts);
Bitmap bm = null;
InputStream is = null;
try {
final TypedValue value = new TypedValue();
is = res.openRawResource(id, value);
bm = decodeResourceStream(res, value, is, null, opts);
} catch (Exception e) {
/* do nothing.
If the exception happened on open, bm will be null.
If it happened on close, bm is still valid.
*/
} finally {
try {
if (is != null) is.close();
} catch (IOException e) {
// Ignore
}
}
if (bm == null && opts != null && opts.inBitmap != null) {
throw new IllegalArgumentException("Problem decoding into existing bitmap");
}
return bm;
}
现在我们来创建一个简单工厂
1.定义一个父类
public interface Model {
public void createModel();
}
2.定义两个实例,实现父类方法
public class AndroidModel implements Model {
@Override
public void createModel() {
System.out.println("this is androidModel!");
}
}
public class IosModel implements Model {
@Override
public void createModel() {
System.out.println("this is iosModel!");
}
}
3.创建简单工厂
public class ModelFactory {
/**
* 单个方法
*/
public Model createModel(String type) {
if ("ios".equals(type)) {
return new IosModel();
} else if ("android".equals(type)) {
return new AndroidModel();
} else {
return null;
}
}
}
/**
* 多个方法
*/
public class ModelFactory {
private Model createAndroidModel() {
return new AndroidModel();
}
private Model createIosModel() {
return new IosModel();
}
}
/**
* 多个静态方法
*/
public class ModelFactory {
private static Model createStaticAndroidModel() {
return new AndroidModel();
}
private static Model createStaticIosModel() {
return new IosModel();
}
}
2.工厂模式
简单工厂模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,如何解决?就用到工厂方法模式,创建一个工厂接口和创建多个工厂实现类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了。
1.创建对应的工厂
public interface ClientFactory {
public Model produce();
}
public class IosClientFactory implements ClientFactory{
@Override
public Model produce() {
return new IosModel();
}
}
public class AndroidClientFactory implements ClientFactory{
@Override
public Model produce() {
return new AndroidModel();
}
}
2.调用工厂方法
private void factoryTeat(){
ClientFactory factory=
new AndroidClientFactory();
Model model= factory.produce();
model.createModel();
}
3.单例模式
在一个进程中保证一个类仅有一个实例,并提供一个访问它的全局访问点。实现的方法为先判断实例存在与否,如果存在则直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象
一般单例我们分为懒汉式和饿汉式,懒汉式即为用到时再去给初始化单例,饿汉式为程序启动时就初始化单例
/*懒汉式*/
public class SingleTest {
/* 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载 */
private static SingleTest instance;
private SingleTest(){
}
public static SingleTest getInstance(){
if(instance==null){
instance=new SingleTest();
}
return instance;
}
}
/*饿汉式*/
public class SingleTest {
/* 持有私有静态实例*/
private static SingleTest instance=new SingleTest();
private SingleTest(){
}
public static SingleTest getInstance(){
return instance;
}
}
虽然这两种单例模式都能满足我们对单例的需求,但是都会有一些问题
饿汉式:在我们不用这个单例时,也进行了初始化,占用不必要的内存资源
懒汉式:当多个线程同时调用时,会出现初始化多次的情况
基于两种情况,进行以下优化
1.方法加锁
public static synchronized SingleTest getInstance2(){
if(instance==null){
instance=new SingleTest();
}
return instance;
}
但是synchronized关键字锁住的是这个对象,这样的用法,在性能上会有所下降,因为每次调用getInstance(),都要对对象上锁。
2.代码加锁
public static SingleTest getInstance3(){
if(instance==null){
synchronized (instance){
if(instance==null){
instance=new SingleTest();
}
}
}
return instance;
}
在Java指令中创建对象和赋值操作是分开进行的,也就是说instance = new Singleton();语句是分两步执行的。但是JVM并不保证这两个操作的先后顺序,也就是说有可能JVM会为新的Singleton实例分配空间,然后直接赋值给instance成员,然后再去初始化这个Singleton实例
SingleTest instance=new SingleTest()
new SingleTest() => instance
优化方式
1.加volatile 字段
Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
private volatile static SingleTest instance;
2.静态内部类 :JVM内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的
public class SingleTest {
private static class SingleTestInstanceHolder{
private static final SingleTest singleInstance=new SingleTest();
}
public static SingleTest getInstance(){
return SingleTestInstanceHolder.singleInstance;
}
}
当我们第一次调用getInstance的时候,JVM能够帮我们保证instance只被创建一次,并且会保证把赋值给instance的内存初始化完毕
4.建造者模式
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示,适用于初始化的对象比较复杂且参数较多的情况。
private void alertBuilder(Context context){
AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setTitle("Title")
.setCancelable(false)
.setIcon(R.mipmap.ic_launcher)
.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
}
}).setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
}
})
.setMessage("Message");
AlertDialog dialog = builder.create();
dialog.show();
}
public class AlertDialog extends Dialog implements DialogInterface {
public static class Builder {
private final AlertController.AlertParams P;
public Builder(Context context) {
this(context, resolveDialogTheme(context, ResourceId.ID_NULL));
}
public Builder(Context context, int themeResId) {
P = new AlertController.AlertParams(new ContextThemeWrapper(
context, resolveDialogTheme(context, themeResId)));
}
public Builder setTitle(CharSequence title) {
P.mTitle = title;
return this;
}
public Builder setMessage(CharSequence message) {
P.mMessage = message;
return this;
}
public AlertDialog create() {
/ Context has already been wrapped with the appropriate theme.
final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);
P.apply(dialog.mAlert);
dialog.setCancelable(P.mCancelable);
if (P.mCancelable) {
dialog.setCanceledOnTouchOutside(true);
}
dialog.setOnCancelListener(P.mOnCancelListener);
dialog.setOnDismissListener(P.mOnDismissListener);
if (P.mOnKeyListener != null) {
dialog.setOnKeyListener(P.mOnKeyListener);
}
}
}
}
举一个甜筒的列子package com.yuyin.live.room.entity:
房间公屏聊天内容构成
以前的写法
public class HnRoomPublicChatBean {
private int itemType;
private String userId;
private String userName;
private String userAvater;
public HnRoomPublicChatBean(int itemType, String userId) {
this.itemType = itemType;
this.userId = userId;
}
public HnRoomPublicChatBean(int itemType, String userId, String userName) {
this.itemType = itemType;
this.userId = userId;
this.userName = userName;
}
public HnRoomPublicChatBean(int itemType, String userId, String userName, String userAvater) {
this.itemType = itemType;
this.userId = userId;
this.userName = userName;
this.userAvater = userAvater;
}
}
优化之后
public class HnRoomPublicChatBean {
/*
基础参数
*/
private int itemType;
private String userId;
private String userName;
private String userAvater;
...
/**
构造函数
*/
public HnRoomPublicChatBean(Builder builder){
this.itemType = builder.itemType;
this.userId = builder.userId;
this.userName = builder.userName;
this.userAvater = builder.userAvater;
...
}
/**
建造者
*/
public static class Builder{
private int itemType;
private String userId;
private String userName;
private String userAvater;
...
/**
建造
*/
public HnRoomPublicChatBean build() {
return new HnRoomPublicChatBean(this);
}
public Builder setUserId(String userId) {
this.userId = userId;
return this;
}
public Builder setUserNumber(String userNumber) {
this.userNumber = userNumber;
return this;
}
public Builder setUserName(String userName) {
this.userName = userName;
return this;
}
...
}
}
//#############使用
new HnRoomPublicChatBean.Builder()
.setItemType(HnRoomPublicChatBean.MSG_EGG_HAMMER)
.setUserId("用户ID")
.setUserName("用户昵称")
.setUserAvater("用户头像")
...
.build()
5.观察者模式
当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化!
首先我们看看经常用到的观察者RecyclerViewDataObserver
MyAdapter adapter = new MyAdapter();
RecyclerView recyclerView=new RecyclerView(this)
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();
我们先看看Adapter做了什么
public abstract static class Adapter {
private final AdapterDataObservable mObservable = new AdapterDataObservable();
public void onBindViewHolder(@NonNull VH holder, int position,
@NonNull List
然后我们看看RecyclerView以及setAdapter做了什么
···
private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
public void setAdapter(@Nullable Adapter adapter) {
// bail out if layout is frozen
setLayoutFrozen(false);
setAdapterInternal(adapter, false, true);
processDataSetCompletelyChanged(false);
requestLayout();
}
private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious,
boolean removeAndRecycleViews) {
if (mAdapter != null) {
mAdapter.unregisterAdapterDataObserver(mObserver);
mAdapter.onDetachedFromRecyclerView(this);
}
...
mAdapter = adapter;
if (adapter != null) {
adapter.registerAdapterDataObserver(mObserver);
adapter.onAttachedToRecyclerView(this);
}
...
}
static class AdapterDataObservable extends Observable {
···
public void notifyChanged() {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
···
}
private class RecyclerViewDataObserver extends AdapterDataObserver {
···
@Override
public void onChanged() {
assertNotInLayoutOrScroll(null);
mState.mStructureChanged = true;
processDataSetCompletelyChanged(true);
if (!mAdapterHelper.hasPendingUpdates()) {
requestLayout();
}
}
···
}
那如果我们自己写一个观察者应该怎么做呢
创建一个观察者(接口或者抽象类)
public interface Observer {
void update();
}
实现观察者接口,处理数据变化之后的操作
public class ObserverOne implements Observer {
@Override
public void update() {
Log.e("observer1","update");
}
}
public class ObserverTwo implements Observer {
@Override
public void update() {
Log.e("observer2","update");
}
}
定义一个接口,用于注册和解绑观察者
public interface Users {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notifyDataSetChanged();
void updateData();
}
实现类
public class UsersObj implements Users {
private Vector vector=new Vector();
@Override
public void addObserver(Observer observer) {
vector.add(observer);
}
@Override
public void removeObserver(Observer observer) {
vector.remove(observer);
}
@Override
public void notifyDataSetChanged() {
Enumeration elements = vector.elements();
while (elements.hasMoreElements()){
elements.nextElement().update();
}
}
@Override
public void updateData() {
Log.e("updateData","user");
notifyDataSetChanged();
}
private void test(){
Users users=new ChinaUsers();
users.addObserver(new ObserverOne());
users.addObserver(new ObserverTwo());
users.notifyDataSetChanged();
}
}