设计模式

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<VH extends ViewHolder> {
    private final AdapterDataObservable mObservable = new AdapterDataObservable();
    
    public void onBindViewHolder(@NonNull VH holder, int position,
                                 @NonNull List<Object> payloads) {
        onBindViewHolder(holder, position);
    }

    @NonNull
    public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
        try {
            TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
            final VH holder = onCreateViewHolder(parent, viewType);
            if (holder.itemView.getParent() != null) {
                throw new IllegalStateException("ViewHolder views must not be attached when"
                        + " created. Ensure that you are not passing 'true' to the attachToRoot"
                        + " parameter of LayoutInflater.inflate(..., boolean attachToRoot)");
            }
            holder.mItemViewType = viewType;
            return holder;
        } finally {
            TraceCompat.endSection();
        }
    }

    public final void bindViewHolder(@NonNull VH holder, int position) {
        holder.mPosition = position;
        if (hasStableIds()) {
            holder.mItemId = getItemId(position);
        }
        holder.setFlags(ViewHolder.FLAG_BOUND,
                ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
                        | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
        TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);
        onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
        holder.clearPayload();
        final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
        if (layoutParams instanceof RecyclerView.LayoutParams) {
            ((LayoutParams) layoutParams).mInsetsDirty = true;
        }
        TraceCompat.endSection();
    }
    
  
    public final boolean hasObservers() {
        return mObservable.hasObservers();
    }

  
    public void registerAdapterDataObserver(@NonNull AdapterDataObserver observer) {
        mObservable.registerObserver(observer);
    }

   
    public void unregisterAdapterDataObserver(@NonNull AdapterDataObserver observer) {
        mObservable.unregisterObserver(observer);
    }

    
    public final void notifyDataSetChanged() {
        mObservable.notifyChanged();
    }

 
    public final void notifyItemChanged(int position) {
        mObservable.notifyItemRangeChanged(position, 1);
    }

   
    public final void notifyItemRangeChanged(int positionStart, int itemCount) {
        mObservable.notifyItemRangeChanged(positionStart, itemCount);
    }
}

然后我们看看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<AdapterDataObserver> {
    ···
    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<Observer> 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<Observer> 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();
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容