7-《设计模式》

一.mvc/mvp/mvvm

MVC:Model-View-Controller,是一种分层解偶的框架,Model层提供本地数据和网络请求,View层处理视图,Controller处理逻辑,存在问题是Controller层和View层的划分不明显,Model层和View层的存在耦合。

MVP:Model-View-Presenter,是对MVC的升级,Model层和View层与MVC的意思一致,但Model层和View层不再存在耦合,而是通过Presenter层这个桥梁进行交流。

MVVM:Model-View-ViewModel,不同于上面的两个框架,ViewModel持有数据状态,当数据状态改变的时候,会自动通知View层进行更新。

MVC和MVP的区别是什么?

MVP是MVC的进一步解耦,简单来讲,在MVC中,View层既可以和Controller层交互,又可以和Model层交互;而在MVP中,View层只能和Presenter层交互,Model层也只能和Presenter层交互,减少了View层和Model层的耦合,更容易定位错误的来源。

MVVM和MVP的最大区别在哪?

MVP中的每个方法都需要你去主动调用,它其实是被动的,而MVVM中有数据驱动这个概念,当你的持有的数据状态发生变更的时候,你的View你可以监听到这个变化,从而主动去更新,这其实是主动的。

严格来说这三种都不是设计模式,只能算是框架,或者一种思想。每种模式也没有严格定义,不同的人有不同的理解

二. 常见设计模式

2.1.设计模式的六大原则:

  • 单一原则:一个类或者一个方法只负责一项职责,尽量做到类的只有一个行为原因引起变化;
  • 里氏替换原则:子类可以扩展父类的功能,但不能改变原有父类的功能;
  • 依赖倒置原则:上层模块不应该依赖下层模块,两者应依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象;
  • 接口隔离原则:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上面
  • 迪米特原则:最少知道原则,尽量降低类与类之间的耦合;每个类尽量减少对其他类的依赖,减少对外暴露方法,使得功能模块独立,低耦合
  • 开闭原则:对于扩展是开放的,对于修改是封闭的

总结:
单一职责原则告诉我们实现类要职责单一;
里氏替换原则告诉我们不要破坏继承体系;
依赖倒置原则告诉我们要面向接口编程;
接口隔离原则告诉我们在设计接口的时候要精简单一;
迪米特法则告诉我们要降低耦合;
而开闭原则是总纲,他告诉我们要对扩展开放,对修改关闭。

2.2.单例模式

关键点:某个类只能有一个实例,提供一个全局的访问点。

饿汉式:

public class SingleInstance {
	//有一个静态属性instance,在JVM虚拟机装载类信息的时候,会new SingleInstance()对其进行初始化。
	//线程安全,但是没有延迟加载,浪费资源
    private SingleInstance() {}//构造函数私有化
    private final static SingleInstance instance=new SingleInstance();
    public static  SingleInstance getInstance() {
        return instance;
    }
}

懒汉式

public class SingleInstance {
    private SingleInstance() {}
    private  static SingleInstance instance;
    public static SingleInstance getInstance() {
    	//假如2个线程同时进入if 语句,会创建两个对象,线程不安全.
        if (instance==null){
            instance=new SingleInstance();
        }
        return instance;
    }
}

改进:

public class SingleInstance {
    private SingleInstance() {}
    private  static SingleInstance instance;
    public static synchronized SingleInstance getInstance() {
        if (instance==null){
            instance=new SingleInstance();
        }
        return instance;
    }
}
//或者
public class SingleInstance {
    private SingleInstance() {}
    private  static SingleInstance instance;
    public static  SingleInstance getInstance() {
        synchronized(SingleInstance.class){
            if (instance==null){
                instance=new SingleInstance();
            }
        }
        return instance;
    }
}

synchronized 是比较耗费性能的,我们每次调用这个 getInstance()方法的时候,都会进入 synchronized 包裹的代码块内,即使这个时候单例对象已经生成,不再需要创建对象也会进入 synchronized 内部,造成不必要的同步开销。

双重锁定(DCL模式)(重要)

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

看起来已经能够实现懒加载和线程安全了,但是还存在一个问题,那就是没有考虑到 JVM 编译器的指令重排序.

DCL模式会有什么问题?

对象生成实例的过程中,大概会经过以下过程:

  • 1.为对象分配内存空间。
  • 2.初始化对象中的成员变量。
  • 3.将对象指向分配的内存空间(此时对象就不为null)。

由于Jvm会优化指令顺序,也就是说2和3的顺序是不能保证的。在多线程高并发的情况下,当一个线程完成了1、3过程后,当前线程的时间片已用完,这个时候会切换到另一个线程,另一个线程调用这个单例,会使用这个还没初始化完成的实例。

解决方法是使用volatile关键字:

优化后的DCL模式(非常重要)

public class SingleInstance {
	private static volatile SingleInstance instance;//volatile 可以禁止指令重排序
	private SingleInstance() {}
	public static SingleInstance getInstance() {
		if(instance == null) {
			synchronized (SingleInstance.class) {
				if(instance == null) {
					instance = new SingleInstance();
				}
			}
		}
		return instance;
	}
}

上面的分析,会发现懒加载和线程安全是我们自己通过加锁和 volatile 关键字实现的,那么有没有让 JVM 帮我们实现线程安全和懒加载呢?

静态内部类单例(非常重要)

public class SingleInstance {
	private SingleInstance() {}
	public static SingleInstance getInstance() {
		return SingleHolder.instance;
	}
	
	private static class SingleHolder{
		//静态初始化器,由JVM来保证线程安全
		private static final SingleInstance instance = new SingleInstance();
	}
}

首先在 JVM 进行类加载的时候,只是加载了 SingleInstance 类,并不会去执行其中的静态方法,也不会去加载 SingleInstance 内的静态内部类 SingleHolder。所以也就是并不会在初次类加载的时候创建单例对象。
在我们使用getInstance()的时候,我们使用 SingleHolder的静态属性,这个时候会对 SingleHolder 这个静态内部类进行加载,这个时候,就回到了第一种写法 饿汉式中的原理,在类加载的初始化阶段,会对创建单例对象,并且赋值给 INSTANCE 属性。同样,这些操作是发生在类加载阶段的,由 JVM 保证了线程安全,并且是在使用的时候进行加载的,也实现了懒加载。

缺点就是:初始化的时候没法传值给单例类。这个时候就可以使用上面优化后的DCL模式。

枚举单例

public enum SingletonEnum {
	INSTANCE
}

// 获取单例对象

SingletonEnum .INSTANCE// 假如枚举类中有一个方法 getString(),就可以这样调用
SingletonEnum .INSTANCE.getString()

缺点:

不能懒加载
运行时占用内存比非枚举的大很多

2.3.建造者模式

关键点:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示,适用于初始化的对象比较复杂且参数较多的情况。

当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用构造者模式。

Retrofit 和 OkHttp 等开源库,大多都是采用的构造者模式去实现的。

如何实现:假设要创建一个Computer对象:

  • 1.在Computer中创建一个静态内部类Builder,然后将Computer中的参数都复制到Builder类中。
  • 2.在Computer中创建一个private的构造函数,参数为Builder类型
  • 3.在Builder中创建一个public的构造方法,参数为Computer中必填的那些参数,cpu和ram。
  • 4在Builder中创建设置方法,对Computer中那些可选参数进行赋值,返回值为Builder类型的实例
  • 5在Builder中创建一个build()方法,在其中构建Computer的实例并返回

java版本:

public class Computer {
    private  String cpu;
    private  String ram;
    private  int usbCount;
    private  String keyboard;
    private  String display;

    public Computer() {
        this(new Builder());
    }

    public Computer(Builder builder) {
        this.cpu = builder.cpu;
        this.ram = builder.ram;
        this.usbCount = builder.usbCount;
        this.keyboard = builder.keyboard;
        this.display = builder.display;
    }

    @Override
    public String toString() {
        return "Computer{" +
                "cpu='" + cpu + '\'' +
                ", ram='" + ram + '\'' +
                ", usbCount=" + usbCount +
                ", keyboard='" + keyboard + '\'' +
                ", display='" + display + '\'' +
                '}';
    }

    public static class Builder{
        private  String cpu;//必须
        private  String ram;//必须
        private  int usbCount;//可选
        private  String keyboard;//可选
        private  String display;//可选

        public Builder() {
            cpu = "默认cpu";
            ram = "默认ram";
            usbCount = 2;
            keyboard = "默认keyboard";
            display = "默认display";

        }

        public Builder setCpu(String cpu) {
            this.cpu = cpu;
            return this;
        }

        public Builder setRam(String ram) {
            this.ram = ram;
            return this;

        }

        public Builder setUsbCount(int usbCount) {
            this.usbCount = usbCount;
            return this;

        }

        public Builder setKeyboard(String keyboard) {
            this.keyboard = keyboard;
            return this;
        }

        public Builder setDisplay(String display) {
            this.display = display;
            return this;
        }
        public Computer build(){
            return new Computer(this);
        }

    }
}

使用:

var computer = Computer.Builder()
                .setCpu("新款cpu")
                .build()
Log.d("ytx", "computer: "+computer)

kotlin版本:

class KComputer(val cpu: String, val ram: String, val usbCount: Int, val keyboard: String, val display: String) {

    constructor(builder: Builder):this(
        builder.cpu,
        builder.ram,
        builder.usbCount,
        builder.keyboard,
        builder.display
    )
    override fun toString(): String {
        return "Computer(cpu='$cpu', ram='$ram', usbCount=$usbCount, keyboard='$keyboard', display='$display')"
    }

    class Builder{
        var cpu: String = "默认cpu"
            private set
        var ram: String = "默认ram"
            private set
        var usbCount: Int = 2
            private set
        var keyboard: String = "默认keyboard"
            private set
        var display: String = "默认display"
            private set
        fun setCup(inputCup: String) = apply {
            this.cpu = inputCup
        }
        fun setRam(inputRam: String) = apply {
            this.ram = inputRam
        }
        fun setUsb(inputUsb: Int) = apply {
            this.usbCount = inputUsb
        }
        fun setKeyboard(inputKeyboard: String) = apply {
            this.keyboard = inputKeyboard
        }
        fun setDisplay(inputDisplay: String) = apply {
            this.display = inputDisplay
        }
        fun build() = KComputer(this)

    }
}

使用

 var kComputer = KComputer.Builder()
                .setCup("新新的cpu")
                .build()
        Log.d("ytx", "kComputer: "+kComputer)

2.4.责任链模式

关键点:将请求的发送者和接收者解耦,使的多个对象都有处理这个请求的机会。
举例:

1.View的事件分发

  • a). 事件收集之后最先传递给 Activity, 然后依次向下传递,大致如下:
Activity> PhoneWindow> DecorView> ViewGroup> ...> View
  • b). 如果没有任何View消费掉事件,那么这个事件会按照反方向回传,最终传回给Activity,如果最后 Activity 也没有处理,本次事件才会被抛弃:
Activity <PhoneWindow <DecorView <ViewGroup <... <View

这是一个非常经典的责任链模式,如果我能处理就拦截下来自己干,如果自己不能处理或者不确定就交给责任链中下一个对象。

2.Okhttp源码中的责任链模式

OkHttp 的拦截器就是基于责任链模式,每个节点有自己的职责,同时可以选择是否把任务传递给下一个环节

2.1 Inteceptor

主要方法 Intercept。会传递一个 Chain 对象过来,可以在 Chain 在执行 proceed 的前后添加代码。

2.2 Chain

主要方法 proceed。OkHttp 的唯一实现类是 RealInterceptorChain。内部维护了所有要执行的拦截器列表,在 proceed 内部会唤醒下一个 Interceptor ,调用 intercept 来进行下一步:

public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream,
    Connection connection) throws IOException {
  ...
  RealInterceptorChain next = new RealInterceptorChain(
      interceptors, streamAllocation, httpStream, connection, index + 1, request);
  Interceptor interceptor = interceptors.get(index);
  Response response = interceptor.intercept(next);
  ...
  return response;
}

可以看到,RealInterceptorChain的process方法中,会生成一个RealInterceptorChain对象,且注意到index+1,即生成下一个Chain对象,并且同时获取拦截器集合里的下一个拦截器,调用它的intercept,将下一个Chain(next)作为参数传给他去处理,回顾到刚才上面说的,拦截器的interpect里面调用了chain的process,也就是说,每一个拦截器都会持有下一个拦截器的chain对象,并通过chain的process方法,触发RealInterceptorChain里的index下标再+1,从而串联起整个拦截链。

2.5.观察者模式

关键点:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。(被观察者可以添加一堆观察者,被观察着发生变化,观察者做出响应)

Android中我们遇到的最常用的观察者模式

1.各种控件的监听,如下:

 //注册观察者
 button.setOnClickListener(new View.OnClickListener() {
        //观察者实现
        @Override
        public void onClick(View arg0) {
            Log.d("test", "Click button ");
        }
    });

button就是被观察者;new出来的View.OnClickListenerd对象就是具体的观察者。在这里OnClickListener是个接口,也就是抽象观察者;通过setOnClickListener把观察者注册到被观察者中。

一旦button捕获的点击事件就会通过回调注册的OnClickListener观察者的onClick方法会来通知观察者。

2.Adapter的notifyDataSetChanged()方法

当我们使用ListView时,需要更新数据时我们就会调用Adapter的notifyDataSetChanged()方法,那么我们来看看notifyDataSetChanged()的实现原理,这个方法是定义在BaseAdaper中,具体代码如下:

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
     //数据集被观察者
    private final DataSetObservable mDataSetObservable = new DataSetObservable();

    //注册观察者
    public void registerDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.registerObserver(observer);
    }
    //注销观察者
    public void unregisterDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.unregisterObserver(observer);
    }
    //数据集改变时,通知所有观察者
    public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }
}
    //其他代码略

上面的代码可以看出BaseAdapter实际上就是使用了观察者模式,BaseAdapter就是具体的被观察者。接下来看看 mDataSetObservable.notifyChanged()的实现:
//数据集被观察者

public class DataSetObservable extends Observable<DataSetObserver> {
   
    public void notifyChanged() {
        synchronized(mObservers) {
            //遍历所有观察者,并调用他们的onChanged()方法
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }
    //其他代码略
}

AdapterDataSetObserver类中的onChanged()方法没看出啥,继续看他父类的onChanged()方法:

class AdapterDataSetObserver extends DataSetObserver {
        private Parcelable mInstanceState = null;
        //观察者的核心实现
        @Override
        public void onChanged() {
            mDataChanged = true;
            mOldItemCount = mItemCount;
            mItemCount = getAdapter().getCount();//获取Adapter中的数据的数量
            if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
                    && mOldItemCount == 0 && mItemCount > 0) {
                AdapterView.this.onRestoreInstanceState(mInstanceState);
                mInstanceState = null;
            } else {
                rememberSyncState();
            }
            checkFocus();
            //重新布局
            requestLayout();
        }

       //其他代码略
    }

最终就是在AdapterDataSetObserver这个类里面的**onChanged()**方法中实现了布局的更新。

简单总结:

当ListView的数据发生变化时,我们调用Adapter的notifyDataSetChanged()方法,这个方法又会调用所有观察者(AdapterDataSetObserver)的onChanged()方法,onChanged()方法又会调requestLayout()方法来重新进行布局。

3. BroadcastReceiver

4.RxJava、RxAndroid、EventBus、otto等等,也是使用了观察者模式。

2.6.代理模式

为其他的对象提供一种代理以控制对这个对象的访问。适用于当无法或不想直接访问某个对象时通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口。

静态代理:
静态代理很好理解就是我们需要编写一个代理类。实现我们需要代理的所有方法。所以称之为静态代理。

动态代理:
在java的动态代理机制中,有两个重要的类或接口

一个是 InvocationHandler(Interface),另一个则是Proxy(Class),这一个类和接口是实现我们动态代理所必须用到的

Proxy这个类的 newProxyInstance 这个方法:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

oader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces:一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上。

InvocationHandler:

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
proxy:指代生成的代理对象;
method:指代的是我们所要调用真实对象的某个方法的Method对象;
args:指代的是调用真实对象某个方法时接受的参数;
每一个代理实例类的InvocationHandler 都要实现InvocationHandler这个接口。并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke 方法来进行调用

示例:

// 定义相关接口
public interface BaseInterface {
    void doSomething();
}

// 接口的相关实现类
public class BaseImpl implements BaseInterface {
    @Override
    public void doSomething() {
        System.out.println("doSomething");
    }
}

public static void main(String args[]) {
    BaseImpl base = new BaseImpl();
    // Proxy 动态代理实现
    BaseInterface proxyInstance = (BaseInterface) Proxy.newProxyInstance(base.getClass().getClassLoader(), base.getClass().getInterfaces(), new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (method.getName().equals("doSomething")) {
                method.invoke(base, args);
                System.out.println("do more");
            }
            return null;
        }
    });

    proxyInstance.doSomething();
}

2.7.策略模式

关键点:定义了一系列的算法,并封装起来,提供针对同一类型问题的多种处理方式。
应用场景

同一个问题具有不同算法时,即仅仅是具体的实现细节不同时,如各种排序算法等等。
对客户隐藏具体策略(算法)的实现细节,彼此完全独立;提高算法的保密性与安全性。
一个类拥有很多行为,而又需要使用if-else或者switch语句来选择具体行为时。使用策略模式把这些行为独立到具体的策略类中,可以避免多重选择的结构。

Glide磁盘缓存支持多种策略模式

Glide.with(this).load(url).diskCacheStrategy(DiskCacheStrategy.NONE).into(iv);

DiskCacheStrategy.NONE:表示不缓存任何内容
DiskCacheStrategy.SOURCE:表示只缓存原始图片
DiskCacheStrategy.RESULT:表示只缓存转换后的图片(默认选项)
DiskCacheStrategy.ALL:表示既缓存原始图片,也缓存转换后的图片

1.实现:通过枚举

(1)log输出策略枚举类

enum class LoggerStrategy {
    LOGCAT,FILE,EMAIL,NET
}

(2)ILogger接口,以及对应的四个实现子类

interface ILogger {
    fun d(tag:String,log:String)
    fun v(tag:String,log:String)
    fun i(tag:String,log:String)
    fun w(tag:String,log:String)
    fun e(tag:String,log:String)
    /**
     * 默认TAG
     */
    fun setLogTAG(TAG:String)
    /**
     * Log存储位置,可以是本地路径 可以是网络路径 可以是Email地址
     */
    fun setSaveUrl(url:String)
}
class LogcatLogger: ILogger {
    private var TAG = ""
    private var loggerFormat = "(:=>$TAG %s -->%s"
    private var url = ""
    override fun d(tag: String, log: String) {
            Log.d(TAG,String.format(loggerFormat,tag,log))
    }

    override fun v(tag: String, log: String) {
            Log.v(TAG,String.format(loggerFormat,tag,log))
    }

    override fun i(tag: String, log: String) {
            Log.i(TAG,String.format(loggerFormat,tag,log))
    }

    override fun w(tag: String, log: String) {
            Log.w(TAG,String.format(loggerFormat,tag,log))
    }

    override fun e(tag: String, log: String) {
            Log.e(TAG,String.format(loggerFormat,tag,log))
    }
    override fun setLogTAG(TAG: String) {
       this.TAG = TAG
    }
    override fun setSaveUrl(url: String) {
        this.url = url
    }
}
class FileLogger: ILogger {
    private var TAG = ""
    private var url = ""
    override fun d(tag: String, log: String) {
        //写到对应的磁盘文件中
    }

    override fun v(tag: String, log: String) {
        //写到对应的磁盘文件中
    }

    override fun i(tag: String, log: String) {
        //写到对应的磁盘文件中
    }

    override fun w(tag: String, log: String) {
        //写到对应的磁盘文件中
    }

    override fun e(tag: String, log: String) {
        //写到对应的磁盘文件中
    }
    override fun setLogTAG(TAG: String) {
        this.TAG = TAG
    }
    override fun setSaveUrl(url: String) {
        this.url = url
    }
}

(3)Logger:采用策略模式创建对应的Logger

class Logger private constructor (private val TAG:String, private val loggerStrategy: LoggerStrategy, private val saveUrl:String) {
    private var logger:ILogger
    init {
        logger = getLogger()
    }
    /**
     * 创建Logger
     */
    fun getLogger():ILogger{
        when(loggerStrategy){
            LoggerStrategy.EMAIL -> throw IllegalArgumentException("该方案未实现")
            LoggerStrategy.FILE -> logger = FileLogger()
            LoggerStrategy.NET -> throw IllegalArgumentException("该方案未实现")
            LoggerStrategy.LOGCAT -> logger = LogcatLogger()
        }
        logger.setLogTAG(TAG)
        logger.setSaveUrl(saveUrl)
        return logger
    }

    /**
     * 输出Debug log
     */
    fun d(Tag:String,log:String){
        logger.d(Tag,log)
    }

    /**
     * 输出Warn log
     */
    fun w(Tag:String,log:String){
        logger.w(Tag,log)
    }

    /**
     * 输出Verbose log
     */
    fun v(Tag:String,log:String){
        logger.v(Tag,log)
    }

    /**
     * 输出Error log
     */
    fun e(Tag:String,log:String){
        logger.e(Tag,log)
    }
    /**
     * 输出Info log
     */
    fun i(Tag:String,log:String){
        logger.i(Tag,log)
    }


}

2.通过对象实现:看Glide源码中DiskCacheStrategy类

2.8.工厂模式

工厂模式分为三种 :简单工厂模式 、工厂方法模式 、抽象工厂模式 。

简单工厂模式:

	//抽象产品类 
    public abstract class Product {
        public abstract void show();
    }
  //具体产品类A 
    public class ProductA extends Product {
        @Override
        public void show() {
            System.out.println("product A");
        }
    }
    //具体产品类B
    public class ProductB extends Product {
        @Override
        public void show() {
            System.out.println("product B");
        }
    }
  //具体产品类A 
    public class ProductA extends Product {
        @Override
        public void show() {
            System.out.println("product A");
        }
    }
    //具体产品类B
    public class ProductB extends Product {
        @Override
        public void show() {
            System.out.println("product B");
        }
    }
//创建工厂类,创建具体的产品:
public class Factory {
    public static Product create(String productName) {
        Product product = null;
        //通过switch语句控制生产哪种商品
        switch (productName) {
            case "A":
                product = new ProductA();
                break;
            case "B":
                product = new ProductB();
                break;
        }
        return product;
    }
}
 public void test() {
        Factory.create("A").show();//生产ProductA
        Factory.create("B").show();//生产ProductB
        try {
            Factory.create("C").show();//生产ProductC
        } catch (NullPointerException e) {
            System.out.println("没有ProductC");//没有ProductC,会报错
        }
    }

缺点

  • 1.违背开放封闭原则,若需添加新产品则必须修改工厂类逻辑,会造成工厂逻辑过于复杂。

  • 2.简单工厂模式使用了静态工厂方法,因此静态方法不能被继承和重写。

  • 3.工厂类包含了所有实例(产品)的创建逻辑,若工厂类出错,则会造成整个系统都会会受到影响。
    工厂方法模式与简单工厂模式比较

  • 1.工厂方法模式有抽象工厂类,简单工厂模式没有抽象工厂类且其工厂类的工厂方法是静态的。

  • 2.工厂方法模式新增产品时只需新建一个工厂类即可,符合开放封闭原则;而简单工厂模式需要直接修改工厂类,违反了开放封闭原则。
    工厂方法模式

创建抽象产品类,定义公共接口:

  //抽象产品类
    public abstract class Product {
        public abstract void show();
    }

创建具体产品类,继承Product类:

//具体产品类A 
public class ProductA extends Product {
    @Override
    public void show() {
        System.out.println("product A");
    }
}
//具体产品类B
public class ProductB extends Product {
    @Override
    public void show() {
        System.out.println("product B");
    }
}

创建抽象工厂类,定义公共接口:

//抽象工厂类
public abstract class Factory {
    public abstract Product create();
}

创建具体工厂类,继承抽象工厂类,实现创建具体的产品:

  //具体工厂类A
    public class FactoryA extends Factory {
        @Override
        public Product create() {
            return new ProductA();//创建ProductA
        }
    }
    //具体工厂类B
    public class FactoryB extends Factory {
        @Override
        public Product create() {
            return new ProductB();//创建ProductB
        }
    }
 public void test() {
        //产品A
        Factory factoryA = new FactoryA();
        Product productA = factoryA.create();
        productA.show();
        //产品B
        Factory factoryB = new FactoryB();
        Product productB = factoryB.create();
        productB.show();
    }

应用场景
生成复杂对象时,无需知道具体类名,只需知道相应的工厂方法即可。
优点

符合开放封闭原则。新增产品时,只需增加相应的具体产品类和相应的工厂子类即可。
符合单一职责原则。每个具体工厂类只负责创建对应的产品。

Android中的ThreadFactory就是使用了工厂方法模式来生成线程的,线程就是ThreadFactory的产品。

ThreadFactory相关源码分析

   //抽象产品:Runnable
    public interface Runnable {
        public abstract void run();
    }
    
    //具体产品:Thread
    public class Thread implements Runnable {
        //构造方法
        public Thread(Runnable target, String name) {
            init(null, target, name, 0);
        }
        
        @Override
        //实现抽象产品的抽象方法
        public void run() {
            if (target != null) {
                target.run();
            }
        }
        
        //其他代码略
    }
    
    
    //抽象工厂:ThreadFactory
    public interface ThreadFactory {
        Thread newThread(Runnable r);
    }
    
    //具体工厂:AsyncTask中的实现
    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);
        
        //实现抽象工厂的抽象方法
        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());//返回Thread这个产品
        }
    };
通过ThreadFactory,我们可以创建出不同的Thread来。
同样,我们可以创建另外类似的工厂,生产某种专门的线程,非常容易扩展。

抽象工厂模式

创建抽象产品类

//抽象产品类-- CPU
public abstract class CPU {
    public abstract void showCPU();
}
//抽象产品类-- 内存
public abstract class Memory {
    public abstract void showMemory();
}
//抽象产品类-- 硬盘
public abstract class HD {
    public abstract void showHD();
}

创建具体产品类

    //具体产品类-- Intet CPU
    public class IntelCPU extends CPU {

        @Override
        public void showCPU() {
            System.out.println("Intet CPU");
        }
    }
    
    //具体产品类-- AMD CPU
    public class AmdCPU extends CPU {

        @Override
        public void showCPU() {
            System.out.println("AMD CPU");
        }
    }

    //具体产品类-- 三星 内存
    public class SamsungMemory extends Memory {

        @Override
        public void showMemory() {
            System.out.println("三星 内存");
        }
    }
    
    //具体产品类-- 金士顿 内存
    public class KingstonMemory extends Memory {

        @Override
        public void showMemory() {
            System.out.println("金士顿 内存");
        }
    }

    //具体产品类-- 希捷 硬盘
    public class SeagateHD extends HD {

        @Override
        public void showHD() {
            System.out.println("希捷 硬盘");
        }
    }

    //具体产品类-- 西部数据 硬盘
    public class WdHD extends HD {

        @Override
        public void showHD() {
            System.out.println("西部数据 硬盘");
        }
    }

创建抽象工厂类:定义工厂中用来创建不同产品的方法:

//抽象工厂类,电脑工厂类
public abstract class ComputerFactory {

    public abstract CPU createCPU();

    public abstract Memory createMemory();

    public abstract HD createHD();
}
 public void test() {
        System.out.println("--------------------生产联想电脑-----------------------");
        ComputerFactory lenovoComputerFactory = new LenovoComputerFactory();
        lenovoComputerFactory.createCPU().showCPU();
        lenovoComputerFactory.createMemory().showMemory();
        lenovoComputerFactory.createHD().showHD();

        System.out.println("--------------------生产华硕电脑-----------------------");
        ComputerFactory asusComputerFactory = new AsusComputerFactory();
        asusComputerFactory.createCPU().showCPU();
        asusComputerFactory.createMemory().showMemory();
        asusComputerFactory.createHD().showHD();
        
        System.out.println("--------------------生产惠普电脑-----------------------");
        ComputerFactory hpComputerFactory = new HpComputerFactory();
        hpComputerFactory.createCPU().showCPU();
        hpComputerFactory.createMemory().showMemory();
        hpComputerFactory.createHD().showHD();
    }
--------------------生产联想电脑-----------------------
Intet CPU
三星 内存
希捷 硬盘
--------------------生产华硕电脑-----------------------
AMD CPU
金士顿 内存
西部数据 硬盘
--------------------生产惠普电脑-----------------------
Intet CPU
金士顿 内存
西部数据 硬盘

应用场景

生产多个产品组合的对象时。

优点

代码解耦,创建实例的工作与使用实例的工作分开,使用者不必关心类对象如何创建。

缺点

如果增加新的产品,则修改抽象工厂和所有的具体工厂,违反了开放封闭原则

工厂方法模式与抽象工厂模式比较

在工厂方法模式中具体工厂负责生产具体的产品,每一个具体工厂对应一种具体产品,工厂方法具有唯一性。
抽象工厂模式则可以提供多个产品对象,而不是单一的产品对象。

2.9.适配器模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值