【27】23种设计模式

一、概述

这次来讲一下23种设计模式,这是老生常谈的话了,实际运用中,能熟练并且完全掌握的设计模式,大家估计都寥寥无几。首先需要明白一点,你认为的设计模式的作用是什么?不是别人认为的,也不是百度认为的。而我认为的设计模式,是为了提供给我们更好的在开发设计中,让功能具有可扩展性,灵活性,可复用性的方法。也许在不断地了解这些模式之后,会对这个认识不断优化,得出你最终认为的价值。

如果按照以往方式去讲设计模式,估计就不用讲了,我认为菜鸟教程已经把23种设计模式讲的很明白,大家也可以直接去看教程。因此,这次我决定换个方式来讲一下23种设计模式的应用。让大家看看源码当中是如何运用这些设计模式的。

二、设计模式

由于只是了解设计模式,在贴源码的过程中,会有系统源码、第三方库源码、又或者省略很多关注设计中不重要的代码,可以根据贴的类名去查看源码文件。本次的讲的方式也会依照设计模式分类:创建、结构、行为分开进行书写。

1.单例模式

使用场景:用于创建唯一的对象实例,常见于管理全局状态或资源。
注意:虽然使用静态类也可以实现唯一实例,管理全局。但是在扩展方面:比如继承和多态,内存管理:比如延迟创建,垃圾回收,线程安全等都有限制。

	//WindowManagerGlobal.java
    private WindowManagerGlobal() {
    }
    @UnsupportedAppUsage
    public static WindowManagerGlobal getInstance() {
        synchronized (WindowManagerGlobal.class) {
            if (sDefaultWindowManager == null) {
                sDefaultWindowManager = new WindowManagerGlobal();
            }
            return sDefaultWindowManager;
        }
    }

在WindowManagerImpl中,将操作View的行为交给了WindowManagerGlobal去操作,其中使用的就是单例模式。不过这里使用的(伪)双重锁的方式实现的单例模式,实际上的最佳实现还是使用静态内部类的方式实现单例模式,可以自行了解。

2.工厂模式

使用场景:用于创建对象,通常用户替代直接使用new关键字。
注意:这个模式的了解对后续的抽象工厂模式会有帮助。

//BitmapFactory.jva
   public static Bitmap decodeFile(String pathName) {
        return decodeFile(pathName, null);
    }
    
    public static Bitmap decodeResource(Resources res, int id) {
        return decodeResource(res, id, null);
    }

    public static Bitmap decodeStream(InputStream is) {
        return decodeStream(is, null, null);
    }

和传统的工厂模式有点点区别,这是通过提供多个静态接口来展现的工厂模式,根据传入的资源类型,创建不同的适配的bitmap对象。传统的工厂模式,可能根据传入的tag,name等等来判断创建不同的对象。

可以看到,工厂提供的产品是抽象的,具体的产品是外部使用者决定的,而不需要关注创建的细节,根据传入的参数,决定工厂创建什么具体产品返回。

3.抽象工厂模式

使用场景:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

//LayoutInflater.java
    public static LayoutInflater from(@UiContext Context context) {
        LayoutInflater LayoutInflater =
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        if (LayoutInflater == null) {
            throw new AssertionError("LayoutInflater not found.");
        }
        return LayoutInflater;
    }

    public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
        return inflate(parser, root, root != null);
    }

布局加载泵通过context#getSystemService就是一个创建抽象工厂创建具体工厂的过程,inflate像之前讲到的工厂模式,根据传入的布局xml解析的结果,创建不同的View。

4.建造者模式

使用场景:用于构建复杂对象的各个部分,并允许按步骤创建对象。

//AlertDialog.java
AlertDialog dialog = new AlertDialog.Builder(context)
        .setTitle("Title")
        .setMessage("Message")
        .setPositiveButton("OK", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                // Do something
            }
        })
        .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                // Do something
            }
        })
        .create();
dialog.show();

这里直接给出了使用方式,更容易关注到建造的过程。建造者模式更适合建造出一个组合,各个建造过程中传入的对象都会在组合中承担角色。

5.原型模式

使用场景:用于创建重复的对象,同时又能保证性能。
注意:这里的例子涉及native层的拷贝,不能了解模式的使用过程,建议看菜鸟驿站的原型模式例子理解。

//Bitmap.java
    public Bitmap copy(@NonNull Config config, boolean isMutable) {
        checkRecycled("Can't copy a recycled bitmap");
        if (config == Config.HARDWARE && isMutable) {
            throw new IllegalArgumentException("Hardware bitmaps are always immutable");
        }
        noteHardwareBitmapSlowCall();
        Bitmap b = nativeCopy(mNativePtr, config.nativeInt, isMutable);
        if (b != null) {
            b.setPremultiplied(mRequestPremultiplied);
            b.mDensity = mDensity;
        }
        return b;
    }

这里强调是重复的对象,因为通过构造函数创建对象的过程中,会对字段逐个初始化,特别是对象比较复杂的情况下,克隆(拷贝)可以显著提高性能。

结构型

6.适配器模式

使用场景:用于将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作。
注意:给出的是源码的使用方式,具体可以看JetPack#RecycleView的源码实现和使用方式。

//PopupWindowVisibility.java
        ArrayAdapter<String> autoAdapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_dropdown_item_1line, COUNTRIES);
        AutoCompleteTextView textView = findViewById(R.id.auto);
        textView.setAdapter(autoAdapter);

通过适配器接口,将用户输入的内容,转换成另一个接口适配的内容,这就是适配器工作的原理。

7.桥接模式

使用场景:用于将抽象部分与它的实现部分分离,使它们都可以独立的变化。

//RecyclerView使用
	private RecyclerView recyclerViewA;
	private RecyclerView recyclerViewB;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerViewA = findViewById(R.id.recyclerView1);
        recyclerViewA.setLayoutManager(new LinearLayoutManager(this));
        recyclerViewA.setAdapter(new OneItemAdapter(itemList));

		recyclerViewB = findViewById(R.id.recyclerView2);
		recyclerViewB.setLayoutManager(new LinearLayoutManager(this));
		recyclerViewB.setAdapter(new TwoItemAdapter(itemList));
    }

在分离抽象和实现的前提下,还有就是避免类爆炸,当一个类有多个维度的变化时,会导致类的数量急剧增加,这点很重要。使用桥接模式,我们只需要独立的扩展抽象部分和实现部分。

举例:一个图形类,它可以有不同颜色,或者再多一个维度,如果不使用桥接模式,我们就需要根据形状和颜色的这些维度组合进行排列组合,这样类的增加数量就是指数级的。

8.组合模式

使用场景:用于将对象组合成树形结构以表示“部分-整体”的层次结构。

//ViewGroup.java
@UiThread
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
    @UnsupportedAppUsage
    protected ArrayList<View> mDisappearingChildren;
}

ViewGroup本身继承View,管理了一个集合来存储它的子View。简化树形结构中对象的处理,无论它们是单个对象还是组合对象。

试想一下,建立的ViewTree,通过List管理ViewGroupA、ViewGroupB,它们每个视图组下,又有若干个其他的子视图View,它们当中也有的View可能是一个ViewGroup,包含其他的子视图View,形成的树形结构。

9.装饰模式

使用场景:用于向一个现有对象添加新功能,同时又不改变其结构。
注意:需要不改变原有结构,又添加新功能办法有哪些,比如继承父类、代理模式等。

//RxJava
static final class SubscribeOnObserver<T> extends AtomicReference<Disposable> implements Observer<T>, Disposable {
        final Observer<? super T> downstream;

        SubscribeOnObserver(Observer<? super T> downstream) {
            this.downstream = downstream;
        }

        @Override
        public void onNext(T t) {
            downstream.onNext(t);
        }

        @Override
        public void onError(Throwable t) {
            downstream.onError(t);
        }

        @Override
        public void onComplete() {
            downstream.onComplete();
        }

        @Override
        public void dispose() {
            DisposableHelper.dispose(this);
        }

        @Override
        public boolean isDisposed() {
            return get() == DisposableHelper.DISPOSED;
        }

        void setDisposable(Disposable d) {
            DisposableHelper.setOnce(this, d);
        }
    }

SubscribeOnObserver实现了Observer接口,并接收一个Observer对象,装饰了Observer对象,在原有的行为上,扩展了新的功能。

10.外观模式

使用场景:为子系统中的一组接口提供一个一致的界面,使得这些子系统更加容易使用。

MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource("path/to/media/file");
mediaPlayer.prepare();
mediaPlayer.start();

可以看看菜鸟的原型,其实也很好理解,创建mediaPlayer的过程会伴随其他需要的对象的创建,可以通过MediaPlayer这个外观对象,调用内部其他创建的具体对象的其他方法。

11.享元模式

使用场景:用于减少创建对象的数量,以减少内存占用和提高性能。

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);

通过工厂获取目标对象,工厂会通过列表缓存创建的不同类型对象,查找到可以复用的对象,直接返回,如果没有重新创建再添加到集合中,大概就是这样。

12.代理模式

使用场景:用于创建对象的代表,以控制对其他对象的访问。

// BinderProxy.java
public class BinderProxy extends Binder{
...
}

代理模式看上去和装饰模式的确很像,装饰模式定义了一个抽象类,来接收被代理的对象。可能更重要的区别是,代理模式强调的是操控被代理对象的原有操作,而装饰模式是在原对象上去扩展新的操作。

行为模式

13.责任链模式

使用场景:为请求创建一个接收者对象的链。

//okHttp # RealInterceptorChain.java
public final class RealInterceptorChain implements Interceptor.Chain {
    private final List<Interceptor> interceptors;
    private final int index;
    private final Request request;
    private final Call call;
    private final int connectTimeout;
    private final int readTimeout;
    private final int writeTimeout;

    public RealInterceptorChain(List<Interceptor> interceptors, int index, Request request, Call call, int connectTimeout, int readTimeout, int writeTimeout) {
        this.interceptors = interceptors;
        this.index = index;
        this.request = request;
        this.call = call;
        this.connectTimeout = connectTimeout;
        this.readTimeout = readTimeout;
        this.writeTimeout = writeTimeout;
    }

    @Override
    public Request request() {
        return request;
    }

    @Override
    public Response proceed(Request request) throws IOException {
        if (index >= interceptors.size()) throw new AssertionError();

        // 获取下一个拦截器
        Interceptor nextInterceptor = interceptors.get(index);

        // 创建下一个拦截器链
        RealInterceptorChain nextChain = new RealInterceptorChain(interceptors, index + 1, request, call, connectTimeout, readTimeout, writeTimeout);

        // 调用下一个拦截器的 intercept 方法
        Response response = nextInterceptor.intercept(nextChain);

        return response;
    }

    @Override
    public Connection connection() {
        return null;
    }
}

使用的就是责任链模式,这个应该大家都比较熟悉。定义链式结构的一个抽象类,为每个子任务创建不同的链节点,同时设置下一个链节点对象,最后通过开头的节点调用执行方法,陆续的执行整个责任链。可以在执行任务开始加入一些判断条件添加一下检查规则过滤之类的。

14.命令模式

使用场景:用于将请求封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象,命令模式也支持可撤销的操作。

//Handler
Handler handler = new Handler();
Runnable runable = new Runnable(){
	@Override
	public void run(){
		//
	}
}

特别常见的命令模式,handler发送的消息,可以通过消息的id进行取消,通过把消息对象加入到任务队列中,陆续的执行。

15.解释器模式

使用场景:用于定义语言的文法,并建立一个解释器来解释这个语言中的句子。
略:暂时不能理解到位

16.迭代器模式

//待续

你可能感兴趣的:(设计模式,android,java)