Android进阶之光-设计模式

1. 单例设计模式
package com.hfw.smartfilebrowser.designmode;

import java.util.HashMap;
import java.util.Map;

/**
 * 1. 构造器私有化
 * 2. 通过一个静态方法或者枚举返回单例类对象
 * 3. 确保单例类对象有且只有一个,尤其在多线程情况下
 * 4. 确保单例类对象在反序列化时不会重新构建对象(只在实现序列化接口时需要),当实现了序列化接口需要加上private Object readResolve
 * 方法内部返回那个静态变量,因为静态变量不会被序列化,所以直接返回相当于对象没变
 */
public class Singleton {
    public static void main(String[] args) {
        for (int i=0; i<10; i++) {
            System.out.println(E.INSTANCE);
        }
    }
}

/**
 * 饿汉模式 加载类就进行初始化,可能会用不到导致资源浪费
 */
class A {

    private static A a = new A();

    private A() {
    }

    public static A getInstance() {
        return a;
    }
}

/**
 * 懒汉模式 每次调用getInstance都是同步的,不管b是否已经存在了,不推荐用
 */
class B {

    private static B b;

    private B() {
    }

    public synchronized static B getInstance() {
        if (b == null) {
            b = new B();
        }
        return b;
    }
}

/**
 * 双重校验锁 DCL 一般可以使用
 */
class C {

    // 1.可见性,一个线程改了另一个线程就能读到最新的值 2.禁止指令重排序
    private static volatile C c;

    private C() {
    }

    public static C getInstance() {
        if (c == null) {
            synchronized(C.class) {
                if (c == null) {
                    // 1.分配内存 2.调用构造器初始化成员变量 3. 将堆中的地址赋值给c,其中如果不设置volatile后两个操作可能会反着调用
                    c = new C();
                }
            }
        }
        return c;
    }
}

/**
 * 静态内部类单例模式 推荐使用
 */
class D {

    private D() {
    }

    public static D getInstance() {
        return DHolder.d;
    }

    private static class DHolder {
        private static final D d = new D();
    }
}

/**
 * 枚举单例
 */
enum E {

    INSTANCE;

    public void doSomeThing() {
        System.out.println("单例内部的方法");
    }
}

/**
 * 初始化的时候把所有需要单例的对象加入到map中
 * 使用的时候去里面去,问题是每次取出对象都需要强转
 */
class F {

    private static Map<String, Object> map = new HashMap<>();

    private F() {
    }

    public static void put(String key, Object value) {
        if(!map.containsKey(key)) {
            map.put(key, value);
        }
    }

    public static Object get(String key) {
        return map.get(key);
    }
}
2. 简单工厂设计模式
  • 拥有三种角色,一个工厂类,一个基类,一系列基类的派生类(或实现类),举例写个分享SDK
  • 其使用场景是工厂类负责创建的对象比较少,用户只需要知道传给工厂类的参数就行了,而不需关系具体创建
  • 优点: 用户根据参数获得实例,避免了直接实例化类,降低了耦合性
  • 缺点: 简单工厂需要知道其所有要生成的类型,当类型过多是不适合使用,并且如果需求变动比如我要添加微博分享,那么就得修改工厂类,违背了开闭原则
interface ShareChannel {
    void share();
}
class WeixinChannel implements ShareChannel {

    @Override
    public void share() {

    }
}

class QQChannel implements ShareChannel {

    @Override
    public void share() {

    }
}

class ShareChannelFactory {

    public static ShareChannel createShareChannel(String type) {
        switch (type) {
            case "Weixin":
                return new WeixinChannel();
            case "QQ":
                return new QQChannel();
        }
    }
}
3. 工厂方法模式
  • 拥有4种角色,抽象工厂类、具体工厂类、基类(或接口),把简单工厂的实现修改一下
  • 例子:比如微信分享WXAPIFactory.createWXAPI()、钉钉分享DDShareApiFactory.createDDShareApi()都是使用了工厂方法设计模式
  • 对比简单工厂模式,当需求改变需要增加微博分享时我们只需要创建一个WeiboChannel就行了不需要去改变Factory,没有违背开闭原则
public class ShareTest {
    public static void main(String[] args) {
        CommonShareChannelFactory factory = new CommonShareChannelFactory();
        WeixinChannel channel = factory.createShareChannel(WeixinChannel.class);
        channel.share();
    }
}

interface ShareChannel {

    void share();
}

class WeixinChannel implements ShareChannel {

    @Override
    public void share() {

    }
}

class QQChannel implements ShareChannel {

    @Override
    public void share() {

    }
}

interface ShareChannelFactory {
    <T extends ShareChannel> T createShareChannel(Class<T> clazz);
}


class CommonShareChannelFactory implements ShareChannelFactory {

    @Override
    public <T extends ShareChannel> T createShareChannel(Class<T> clazz) {
    	 // 在这里也可以做些预备操作等
        ShareChannel channel = null;
        try {
            channel = clazz.newInstance();
        } catch (IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
        return (T) channel;
    }
}
4. 建造者模式
  • 主要用于创建一个复杂对象时使用,通过Builder类一步一步创建,比如Android中的AlertDialog其组成非常复杂,比如你可以只设置Title、Message,你也可以值设置Title,有很多种可选的设置,分享功能需要很多的参数,而很多参数都是可选的就可以使用建造者模式
  • 优点 将部件与其组成过程分开,客户端只需要关注需要哪些部件,无需关心其实如何组成的
  • 缺点 产生了多余的Build对象
public class ShareConstructorParams {

    private String title;
    private String content;
    private String url;
    private String imageUrl;

    private ShareConstructorParams(Builder builder) {
        this.title = builder.title;
        this.content = builder.content;
        this.url = builder.url;
        this.imageUrl = builder.imageUrl;
    }

    public static class Builder {
        /**
         * 这里假设就4个参数,实际上参数很多
         */
        private String title;
        private String content;
        private String url;
        private String imageUrl;

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

        public ShareConstructorParams.Builder setContent(String content) {
            this.content = content;
            return this;
        }

        public ShareConstructorParams.Builder setUrl(String url) {
            this.url = url;
            return this;
        }

        public ShareConstructorParams.Builder setImageUrl(String imageUrl) {
            this.imageUrl = imageUrl;
            return this;
        }
        
        public ShareConstructorParams build() {
            return new ShareConstructorParams(this);
        }
    }

    public static void main(String[] args) {
        ShareConstructorParams params = new ShareConstructorParams.Builder()
                .setContent("我是分享内容")
                .setImageUrl("http://xxx")
                .setTitle("我是分享标题")
                .setUrl("http://xxx")
                .build();
    }

}
5. 代理设计模式 分为静态代理和动态代理
  • 静态代理
  • 优点:防止与真实对象之间接触,降低耦合性,易于扩展,客户端可以完全不知道代理对象代理的是哪个真实对象
  • 缺点:如果接口需要增加一个方法那么代理类也得实现该方法,把本来维护一个方法变成了维护两个方法
public class Proxy {

    public static void main(String[] args) {
        BuyerProxy proxy = new BuyerProxy();
        proxy.buy();
    }


    interface IShop {
        void buy();
    }

    static class RealBuyer implements IShop {

        @Override
        public void buy() {
            System.out.println("真实的买家");
        }
    }

    static class BuyerProxy implements IShop {

        private IShop mIShop;

        public BuyerProxy(IShop iShop) {
            mIShop = new RealBuyer()
        }

        @Override
        public void buy() {
            System.out.println("我是代购的");
            mIShop.buy();
        }
    }
}
  • 动态代理
  • 相对于静态代理其代理类不用在代码中写死,但是必须要有一个接口才能进行代理,因为java不支持多继承
package com.hefuwei.lwsbooks.designpattern;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 代理设计模式
 */
public class DynamicProxy {

    public static void main(String[] args) {
        IShop proxy = (IShop) Proxy.newProxyInstance(DynamicProxy.class.getClassLoader(), new Class[] {IShop.class}, new MyInvocationHandler(new RealBuyer()));
        proxy.buy();
    }


    interface IShop {
        void buy();
    }

    static class RealBuyer implements IShop {

        @Override
        public void buy() {
            System.out.println("RealBuyer");
        }
    }

    static class MyInvocationHandler implements InvocationHandler {

        private IShop iShop;

        public MyInvocationHandler(IShop iShop) {
            this.iShop = iShop;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (method.getName().equals("buy")) {
                method.invoke(iShop);
            }
            return null;
        }
    }
}
6. 装饰模式
  • 首先说说与代理模式的区别,前者(代理模式)客户端根据不知道代理对象内部究竟代理着哪个具体对象,后者被装饰的对象客户端完全知道,因为就是客户端传入的。
  • 优点 可以在不改变被装饰类的情况下加强类的功能,或者类似Android里面的ContextWrap类,使其子类Activity、Service不必包含复杂的实现逻辑(比如startActivity)
public class Decorate {

    static abstract class Context {
        abstract void startActivity();
    }

    static class ContextWrap extends Context {

        private Context mBase;

        public ContextWrap(Context context) {
            mBase = context;
        }

        @Override
        void startActivity() {
            mBase.startActivity();
        }
    }

    static class ContextImpl extends Context {

        @Override
        void startActivity() {
            System.out.println("真正的去startActivity");
        }
    }

    public static void main(String[] args) {
        ContextWrap wrap = new ContextWrap(new ContextImpl());
        wrap.startActivity();
    }
}
7. 外观模式
  • 一般在封装SDK或者API时用到,暴露给客户端外观类(该类把子系统的功能进行结合)供客户端使用
  • 优点:客户端无需关心具体的子系统的实现,直接与外观类进行交互,降低了耦合性,这样即使具体的子系统的内容发生改变,用户也不会感知到;同时由于客户端只能使用外观类的功能,而不能使用子系统的其他功能所以加强了安全性
  • 缺点:不符合开闭原则,如果业务发生变更那么可能要直接修改外观类
  • 下面假设用户有一台手机,手机有打电话系统和拍照系统,手机的外观类是MobilePhone,用户只能调用该类中的方法去执行操作,无法调用CallManager的privateFunction方法,因此加强了安全性
public class MobilePhone {

    private CallManager callManager;
    private CameraManager camaraManager;


    public static void main(String[] args) {
        // 用户拥有一个手机,拿它去拍照,打电话
        MobilePhone phone = new MobilePhone();
        phone.takePhotos();
        phone.call();
    }

    public MobilePhone() {
        callManager = new CallManager();
        camaraManager = new CameraManager();
    }

    public void call() {
        callManager.call();
        callManager.hangup();
    }

    public void takePhotos() {
        camaraManager.open();
        camaraManager.takePhotos();
        camaraManager.close();
    }
}

/**
 * 专门用来打电话的子功能块
 */
class CallManager {
    public void call() {
        System.out.println("开始通话");
    }

    public void hangup() {
        System.out.println("结束通话");
        System.out.println("挂断电话");
    }

    public void privateFunction() {
        System.out.println("内部才能调用的功能");
    }
}

class CameraManager {
    public void open() {
        System.out.println("打开相机");
    }

    public void takePhotos() {
        System.out.println("拍照");
    }

    public void close() {
        System.out.println("关闭相机");
    }
}
8. 享元设计模式
  • 是池技术的重要实现技术,可以减少重复对象的创建,降低程序内部的占用,提高性能,一般拥有着以下三个角色 1. 抽象享元角色,同时定义出对象的外部状态和内部状态的接口。2. 具体享元角色,实例的抽象享元角色的业务。 3. 享元工厂,负责管理对象池和创建享元对象(一般内部用Map、SpareArray实现),如下面示例中的Ticket抽象类就是抽象享元角色内部的from、to属于内部状态,level(from to)属于外部状态会随着外界状态变化而变化;TrainTicket属于具体享元角色;TicketFactory属于享元工厂
  • 好像其实就是缓存的一种,比如Java中的String一旦常量池有了就不会重新创建、Android中的Message通过一个链表来缓存、Volley里面的ByteArrayPool、OkHttp里面的连接池
public class FlyWeight {

    /**
     * 票接口现在只有TrainTicket一个子类
     */
    public static abstract class Ticket {
        /**
         * from、to是不会变化的  level是会变化的
         */
        protected String from;
        protected String to;
        protected SeatLevel level;
        protected float price;

        public Ticket(String from, String to, SeatLevel level, float price) {
            this.from = from;
            this.to = to;
            this.level = level;
            this.price = price;
        }

        abstract void showTicketInfo();

    }

    public static void main(String[] args) {
        Ticket ticket1 = TicketFactory.getTicket("杭州东", "绍兴北", SeatLevel.SECOND_CLASS_SEAT);
        ticket1.showTicketInfo();
        Ticket ticket2 = TicketFactory.getTicket("杭州东", "绍兴北", SeatLevel.FIRST_CLASS_SEAT);
        ticket2.showTicketInfo();
        Ticket ticket3 = TicketFactory.getTicket("杭州东", "绍兴北", SeatLevel.SECOND_CLASS_SEAT);
        ticket3.showTicketInfo();
    }


    public static class TrainTicket extends Ticket {


        public TrainTicket(String from, String to, SeatLevel level, float price) {
            super(from, to, level, price);
        }


        public String getFrom() {
            return from;
        }

        public void setFrom(String from) {
            this.from = from;
        }

        public String getTo() {
            return to;
        }

        public void setTo(String to) {
            this.to = to;
        }

        public SeatLevel getLevel() {
            return level;
        }

        public void setLevel(SeatLevel level) {
            this.level = level;
        }

        public float getPrice() {
            return price;
        }

        public void setPrice(float price) {
            this.price = price;
        }

        @Override
        public void showTicketInfo() {
            System.out.println("从" + from + " 到" + to + "的火车票" + level + "价格为" + price);
        }
    }

    public enum SeatLevel {
        BUSINESS_CLASS_SEAT("商务座"),
        FIRST_CLASS_SEAT("一等座"),
        SECOND_CLASS_SEAT("二等座");

        private String desc;

        SeatLevel(String desc) {
        }
    }

    public static class TicketFactory {

        private static HashMap<String, Ticket> map = new HashMap<>();

        static Ticket getTicket(String from, String to, SeatLevel level) {
            String key = from + to + level;
            if (map.containsKey(key)) {
                return map.get(key);
            } else {
                Ticket ticket = new TrainTicket(from, to, level, queryPrice(from, to, level));
                map.put(key, ticket);
                return ticket;
            }
        }

        static float queryPrice(String from, String to, SeatLevel level) {
            return (float) (Math.random() * 200);
        }
    }
}
9. 策略设计模式
  • 使用场景,1. 针对同一类型的问题拥有多种解决方式,仅仅具体行为有差异;2. 有多个派生自同一个类的实现类又需要通过if-else、switch判断使用哪个的
  • 优点 1. Context类依赖于抽象类ISort,不依赖与具体实现,Context只需要知道它操作的是一个ISort类就行了 2. 遵守了开闭原则,当添加了新的策略(ISort)时不需要修改Context类
  • Android中的使用,比如Volley里面的Request,RequestQueue只依赖于抽象的Request类,当有新的Request实现不需要改变RequestQueue的代码;Volley里面的RetryPolicy,同样当有新的RetryPolicy时也不需要修改Request代码;RecyclerView的LayoutManager、Adapter
// 不使用策略模式,sort方面内部需要进行判断并且当需要添加新的排序算法那么就得修改SortUtils不符合开闭原则
public class SortUtils {

    public static final int BUBBLE_SORT = 1;
    public static final int CHOOSE_SORT = 2;

    public static void main(String[] args) {
        List<Number> data = Arrays.asList(2, 4, 5, 2.4, 5.4, 1, 5, 12.2, 4.32, 5.45, 6, 7);
        sort(data, BUBBLE_SORT);
        sort(data, CHOOSE_SORT);
    }


    public static List<Number> sort(List<Number> source, int type) {
        if (type == BUBBLE_SORT) {
            return new BubbleSort().sort(source);
        } else if (type == CHOOSE_SORT) {
            return new ChooseSort().sort(source);
        }
        return null;
    }

}
// 使用了策略模式,当需要添加新的排序算法是不需要改变Context类,遵守了开闭原则
public class Context {

    private ISort mISort;

    public void setISort(ISort iSort) {
        mISort = iSort;
    }

    public static void main(String[] args) {
        Context strategy = new Context();
        List<Number> data = Arrays.asList(2, 4, 5, 2.4, 5.4, 1, 5, 12.2, 4.32, 5.45, 6, 7);
        strategy.setISort(new BubbleSort());
        System.out.println(strategy.sort(data));

        strategy.setISort(new ChooseSort());
        System.out.println(strategy.sort(data));

    }

    public List<Number> sort(List<Number> list) {
        if (mISort == null) {
            throw new RuntimeException();
        }
        return mISort.sort(list);
    }
}
10. 模板方法设计模式
  • 主要有两种角色,1. 一个抽象父类 2. 多个派生自该抽象父类的子类
  • 某个方法有些步骤是固定的,有些步骤是不固定的,就可以使用该模式,下面以BaseActivity为例,View.Draw()就是一个就是使用了目标方法模式
  • 优点 将不变的行为搬移到超类,去除了子类中的重复代码
/**
 * 模板方法模式, 以BaseActivity为例
 */
public abstract class BaseActivity extends AppCompatActivity {


    // 方法为final防止子类重写
    @Override
    protected final void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 步骤一
        initData();
        // 步骤二
        initView();
        // 步骤三
        initEvent();
    }

    /**
     * 设置监听事件,需要可重写
     */
    protected void initEvent() {

    }

    /**
     * 抽象方法,子类必须实现用于设置该Activity的视图
     */
    protected abstract void initView();

    /**
     * 初始化数据,如果需要初始化的话,可以重写该方法
     */
    protected void initData() {

    }
}
11. 观察者设计模式
  • 拥有4种角色,一个Observable接口或类、一个Observer接口、一个Observable的派生类、多个Observable接口的实现类
  • 优点 观察者与被观察者是抽象耦合易于扩展
  • 缺点 由于通知观察者是顺序通知的,如果一个观察者做的事情比较多造成了卡顿,那么就会影响其他后面的观察者
  • 下面的代码微信公众号是被观察者,用户是观察者,其中hfw、cbw订阅了该微信公众号,而ym没有订阅,所以ym不会收到该微信公众号的推送(调用update),此外Android中的RecyclerView.Adapter、Receiver都是使用了观察者模式
/**
 * 观察者模式, 微信公众号是被观察这,用户是观察者,用户订阅了微信公众号,当公众号更新后就会通知所有订阅了的用户
 */
public class ObserverPattern {


    public static void main(String[] args) {
        User hfw = new User("hfw");
        User cbw = new User("cbw");
        User ym = new User("ym");

        WeixinOfficialAccounts accounts = new WeixinOfficialAccounts();
        accounts.addObserver(hfw);
        accounts.addObserver(cbw);
        // 公众号拥有者向公众号中发布了一篇文章
        Article article = new Article();
        article.setText("Hello, World !! Hello, World !! Hello, World !! Hello, World !!");
        article.setPublicTime(new Date().toLocaleString());
        accounts.addArticle(article);
    }


    static class WeixinOfficialAccounts extends Observable {

        public void addArticle(Article article) {
            setChanged();
            notifyObservers(article);
        }
    }

    static class User implements Observer {

        private String mName;

        public User(String name) {
            mName = name;
        }

        @Override
        public void update(Observable o, Object arg) {
            // 当接受到了微信公众号的新消息后该干些什么
            System.out.println(mName + " 接受到了文章" + arg);
        }
    }


    static class Article {

        private String publicTime;
        private String text;

        public String getPublicTime() {
            return publicTime;
        }

        public void setPublicTime(String publicTime) {
            this.publicTime = publicTime;
        }

        public String getText() {
            return text;
        }

        public void setText(String text) {
            this.text = text;
        }

        @NonNull
        @Override
        public String toString() {
            return publicTime + " " + text;
        }
    }
}
// 看看java中的Observable类
public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs;

    public Observable() {
    	 // 使用Vector来保存所有观察者
        obs = new Vector<>();
    }
    /**
     * 添加一个观察者,如果观察者列表中没有该观察者那么就加入
     */
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }
    /**
     * 删除一个观察者
     */
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }
    /**
     * 通知所有观察者,没有数据
     */
    public void notifyObservers() {
        notifyObservers(null);
    }
    /**
     * 通知所有观察者,带上数据
     */
    public void notifyObservers(Object arg) {
        Object[] arrLocal;
        synchronized (this) {
            if (!hasChanged())
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }
        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }
    /**
     * 删除一个观察者
     */
    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }
    /**
     * 用来判断当前是否要通知所有观察者
     */
    protected synchronized void setChanged() {
        changed = true;
    }
    protected synchronized void clearChanged() {
        changed = false;
    }
    public synchronized boolean hasChanged() {
        return changed;
    }
    public synchronized int countObservers() {
        return obs.size();
    }
}
// Observer很简单就一个接口,提供了一个Update方法
public interface Observer {
    void update(Observable o, Object arg);
}
/* output: 
   cbw 接受到了文章2018-12-20 19:56:03 Hello, World !! Hello, World !! Hello, World !! Hello, World !!
   hfw 接受到了文章2018-12-20 19:56:03 Hello, World !! Hello, World !! Hello, World !! Hello, World !! */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值