安卓简陋手册系列1:对象管理

说是原创,但本系列实际上是编程思想,Effective Java,深入理解JVM,以及一些大神
博客的笔记,主要内容就是一些理论问题的实践,一些实践问题容易踩的坑,一些坑的
解决方案 等,纯Java系列有十几篇,后面还会有安卓系列,相关的代码链接:纯java代
码– https://github.com/cowthan/JavaAyo,安卓demo代码–
https://github.com/cowthan/AyoWeibo,后续代码也会重整一下,以配合本系列的
内容。

本系列的部分章节和大部分的格式问题,之后会由Mr.LongFace同学负责,作为IT
行业的新生代,我们俩都是特别谦虚的人,所以欢迎各位尽情批评指正。

后续内容:
5 对象池:对象太重
6 Flyweight享元模式:对象太多
7 备忘录模式:状态保存,对象快照
8 原型模式:是啥来着
9 对象clone
10 序列化
11 四种引用
12 内存模型和垃圾回收
13 对象缓存

1 静态工厂

1.1 套路

//常规模式
public static class Boolean{


    //静态工厂方法
    public static Boolean valueOf(boolean b){
        return new Boolean(b); 
    }

    private boolean v;

    private Boolean(boolean b){
        v = b;
    }

}

//高级模式:限制对象的数目,比如单例,此处的例子是:Boolean基本就只能对应两个值
public static class Boolean{

    //预定义的两个对象,某种意义上算是缓存,使用时可以省去创建对象的过程
    public static final Boolean True = new Boolean(true);
    public static final Boolean False = new Boolean(false);

    //静态工厂方法
    public static Boolean valueOf(boolean b){
        return b ? True : False;
    }

    private boolean v;

    private Boolean(boolean b){
        v = b;
    }

}
  • 静态工厂方法的好处是:

    • 优势1:方法有名字,构造器没有,如BigInteger.probablePrime方法,这就是静态工厂,表示BitInteger(int, int, Random)返回的很可能是素数
    • 对于多种参数形式的构造器,可以各自对应一个静态工厂,并给不同名字,还是参考probablePrime
    • 优势2:可以预先构建对象,如Boolean.TRUE和FALSE,类似Flyweight模式
    • 可以管理对象个数,如单例模式,也可以仿枚举,业务逻辑上的相等的值,只给一个对象
    • 优势3:可以返回任意子类型的对象,返回的父类或者接口引用,具体实现类甚至可以对外隐藏,参考Java Collections Framework中集合接口的32个便利实现
    • (1)Collections里有unmodified, empty, checked, synchronized各8个方法,对应8种不同的集合类型
    • 这8种集合类型就是:Collection, List, Map, SortedMap, NavigableMap, Set, SortedSet, NavigableSet,注意这是接口类型,对外的
    • 返回的实际类型是什么呢,都是以private static class的形式实现的,并未对外公开,所以可以jdk随时修改,提升性能或修改实现
    • (2)EnumSet:其静态工厂方法会根据底层枚举类型大小,返回RegalarEnumSet对象或者JumboEnumSet对象,而且这对外部用户是隐藏的
    • (3)这里又能引出服务器提供者的概念
    • 优势4:简化Map

1.2 服务提供者框架:Service Provider Framework

1 简介

这是从静态工厂好处3里引出来的,对外提供一个接口,客户端依赖于此接口实例,但并不关心具体实现

  • 三大组件:以JDBC为例
    • 服务接口:Service Interface,提供者实现,如Connection
    • 提供者注册API:Provider Registreation API,用来注册实现,让客户端访问,如DriverManager.registerDriver()
    • 服务访问API:Service Access API,客户端用来获取服务实例,这里就是灵活的静态工厂,如DriverManager.getConnection()
    • 服务提供者接口,Service Provider Interface,可选,用来创建服务实例,如果没有这个,就得按照类名注册,并通过反射实例化,如Driver就是这个角色

/** * ================服务接口:Service Interface================== * 一个对外提供服务的接口,并且不同情况,会产生不同的Service对象, * 即通过Service的不同实现,对外提供不同的服务 * */
public interface Service {

    void doService();

}


/** * ================服务提供者接口================== * 用来生成Service对象,注意,如果不使用Provider,则注册到Services的就得是Service实现类的Class对象, * newInstance也只能通过反射来了 * 问题就是Provider实现类应该有几个 * */
public interface Provider {
    Service newService();
}

public class Services {

    private Services(){}

    //================提供者注册API==================//
    //这里要么注册provider对象,要么注册Service实现类的Class,你选吧
    private static final Map<String, Provider> providers = new ConcurrentHashMap<>();
    public static final String DEFAULT_PROVIDER_NAME = "<def>";

    public static void registerDefaultProvider(Provider p){
        registerProvider(DEFAULT_PROVIDER_NAME, p);
    }

    public static void registerProvider(String defaultProviderName, Provider p) {
        providers.put(defaultProviderName, p);
    }

    //================服务访问API==================//
    public static Service newInstance(){
        return newInstance(DEFAULT_PROVIDER_NAME);
    }

    public static Service newInstance(String name) {
        Provider p = providers.get(name);
        if(p == null){
            throw new IllegalArgumentException("No provider registered with name + " + name);
        }
        return p.newService();
    }
}
2 分析
  • 使用场景
    • Service提供了某项工作的接口
    • Services是一个平台:
      • 注册接口:用于注册Service的实现
        • 可以注册Provider,如上例,规避调用Class对象和反射
        • 可以注册Service实现类的Class对象
        • 可以注册Service实现类的实例
      • 访问接口:用于获取Service的实现的实例
    • api作者和用户都可以实现Service和Provider,即提供服务
      • JDBC的服务就是连接数据库,不同的Service实现,对应不同的数据库,也使用了不同的驱动
3 场景实例

2 构建器:建造者模式,Builder

  • 使用场景:参数个数多
    • 原始模式:构造器或者静态工厂形式太多,参数多
    • JavaBeans模式:new一个空对象然后set各种参数,代码不宜管理,set过程中,对象也可能处于不一致状态
    • 把JavaBeans的一组set封装起来,就是一个建造者模式的原始形态,但里面依旧需要对对象的一致性负责
      • 这里说的一致性问题,意思是new完对象,还需要一组set之后,对象才能正常工作,
      • 但set期间,对象已经有了,却不能正常工作,这就是一个危险的状态
      • Builder模式,就不存在这个不一致的状态,因为对象最终还是通过一个构造器出来后就已经可以正常工作了
    • 所以这时使用Builder模式,既能保证JavaBeans的可读性,又能保证原始模式的安全性
    • ImageLoader的初始化,AlertDialog的初始化,都使用了这种模式,参数很多,有些必填(放到Buidler的构造方法),有些选填(作为单独方法)

2.1 简单模式

public class NutritionFacts {

    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;


    public NutritionFacts(Builder builder) {
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }

    public static class Builder{

        //必填的参数,无默认值
        private final int servingSize;
        private final int servings;

        //选填的参数,有默认值
        private int calories = 0;
        private int fat      = 0;
        private int carbohydrate = 0;
        private int sodium = 0;

        public Builder(int servingSize, int servings){
            this.servingSize = servingSize;
            this.servings = servings;
        }

        public Builder calories(int val){ calories = val; return this; }
        public Builder fat(int val){ fat = val; return this; }
        public Builder carbohydrate(int val){ carbohydrate = val; return this;}
        public Builder sodium(int val){ sodium = val; return this; }

        public NutritionFacts build(){
            return new NutritionFacts(this);
        }

    }

}

public static void main(String[] args) {
    NutritionFacts cocacola = new NutritionFacts.Builder(240,  8)
            .calories(100)
            .sodium(35)
            .carbohydrate(27)
            .build();
}

2.2 Builder接口

  • 将Builder抽取出来单独做一个接口,这个接口的对象可以:
    • 创建任意多的对象
    • 其功能就类似于直接传Class对象
    • 但比Class对象的newInstance方法多了类型检查,构造方法保证等
    • 缺点就是要创建N多个Builder类
public interface Builder<T> {

    public T build();

}


package com.cowthan.object_management;

public class NutritionFacts2 {

    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;


    public NutritionFacts2(MyBuilder builder) {
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }

    public static class MyBuilder implements Builder<NutritionFacts2>{

        //必填的参数,无默认值
        private final int servingSize;
        private final int servings;

        //选填的参数,有默认值
        private int calories = 0;
        private int fat      = 0;
        private int carbohydrate = 0;
        private int sodium = 0;

        public MyBuilder(int servingSize, int servings){
            this.servingSize = servingSize;
            this.servings = servings;
        }

        public MyBuilder calories(int val){ calories = val; return this; }
        public MyBuilder fat(int val){ fat = val; return this; }
        public MyBuilder carbohydrate(int val){ carbohydrate = val; return this;}
        public MyBuilder sodium(int val){ sodium = val; return this; }

        public NutritionFacts2 build(){
            return new NutritionFacts2(this);
        }

    }

    public static void main(String[] args) {
        Builder<NutritionFacts2> builder = new NutritionFacts2.MyBuilder(240,  8)
                .calories(100)
                .sodium(35)
                .carbohydrate(27);

        NutritionFacts2 cocacola = builder.build();
    }
}

/* 这里的Builder<NutritionFacts2> builder对象,可以传给任意的抽象工厂方法 */

3 单例模式

  • 怎么能破坏单例的限制
    • 反射:反射出私有构造方法,Enum可自然规避此问题,其他方式得强写检查代码
    • 序列化:将单例序列化,再反序列化,出来就是一个新对象,Enum可自然解决,其他方式使用readResolve方法
    • 安卓里的多进程,会产生多个Application

3.1 饿汉:公有域,或静态工厂

public class Singleton {

    public static final Singleton INSTANCE = new Singleton();
    private Singleton(){}
    private Object readResolve(){ return INSTANCE; }

    public void provideService(){

    }

}

//访问
Singleton.INSTANCE.provideService();
public class Singleton {

    private static final Singleton INSTANCE = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){ return INSTANCE; }
    private Object readResolve(){ return INSTANCE; }

    public void provideService(){

    }

}

//访问
Singleton.getInstance().provideService();

3.2 懒汉:双保险模式

延迟加载
volatile的使用

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

3.3 懒汉:内部类模式

这里的说法是:
SingletonHolder作为一个内部类,会在访问时被加载,所以这里实现了延迟加载,并且内部类可以从语言层面上防止多线程的问题,比双重锁模式优雅的多。

public class Singleton{
   private static class SingletonHolder{
       private static final Singleton INSTANCE = new Singleton();
   }
   private Singleton(){}
   public static Singleton getInstance(){
       return SingletonHolder.INSTANCE;
   }
}

3.4 枚举

按照Effective Java书里说法,这个方法虽然没流行起来,但这个是最佳方式,第2版15页

//直接就能防止反射,防止序列化时生成新类
//是否延迟加载不知道
public enum Singleton {

    INSTANCE;

    public void provideService(){

    }

}

3.5 暴力反射版的单例

public class Singleton {
    private Singleton(){}
    public void doSth(){
        System.out.println("做点什么");
    }
}
public class SingletonFactory {
    private static Singleton singleton;
    //===只实例化一次,使用暴力反射
    static{
        try {
            Class cls = Class.forName(Singleton.class.getName());
            Constructor cons = cls.getDeclaredConstructor();
            cons.setAccessible(true);
            singleton = (Singleton) cons.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Singleton getSingleton(){
        return singleton;
    }

    /** * 扩展:一个项目可以有一个单例构造器,负责生成所有单例对象,只需要传入类型, * 但是需要事先知道有几个单例类型 */
}

4 工厂模式

这节的内容感觉有点不对劲,仅作参考,别太信

  • 前言:
    • 对象成体系,纵向分为产品族,横向分为产品等级
    • 本节参考设计模式之禅,继续沿用女娲造人的例子
    • 纵向的产品族(产品线),这里定为男,女,不同的产品族的产品,应该可以组装成一件高等级产品
      • 如螺丝是一个产品族,分不同型号的螺丝,铁板是一个产品族,分不同型号的铁板,门把手是一个产品族,分不同型号的门把手
    • 横向的产品等级,这里定为黑,白,黄,不同型号的螺丝,不同型号的铁板,这个不同型号就是不同产品等级
    • 所以这里男女就是不同的产品,肤色就是不同的型号,所以类体系如下:
      • IHuman
      • AbstractMan, AbstractWomen作为两个产品族,即男女
      • YellowMan extends AbstractMan作为一个男人的一个产品等级,和BlackMan, WhiteMan一样

更一般化的分析:

抽象工厂模式: 先看看下面的分析,我还没搞明白反正,太乱了,具体问题就在于这个模式怎么扩展

先明白两个概念:
在有多个业务品种,业务分类时,有下面这么几个概念
1、产品族,也叫产品线,不同的产品族的产品,应该可以组装成一件高等级产品,
如螺丝是一个产品族,分不同型号的螺丝,铁板是一个产品族,分不同型号的铁板,
门把手是一个产品族,分不同型号的门把手
——螺丝,铁板,门把手就可以组成一个车门

2、产品等级:不同型号的螺丝,不同型号的铁板,这个不同型号就是不同产品等级

所以对于产品而言:有两种选择

1、当产品族需要可扩展时:
有个:IProduct接口
有N个:AbstractXXProduct, N就是产品族的数量,如Abstract_Luosi,Abstract_Tieban,
Abstract_MenBaShou
对于每个产品族,有M个子类:Luosi_10, Luosi_12, Luosi_20, …,M就是产品等级的数量
——一共有M*N子类, N个抽象类,1个接口

扩展产品族只需要增加一个AbstractXXX, 和M个产品等级的子类

2、当产品等级需要可扩展时:
基本上增加产品族,就相当于增加产品线,
这不容易,但是增加产品等级,不需要扩展产品线,只要在
原先产品线上增加个等级就行

对于工厂而言:
——有个最大接口:AbstractFactory
——生产一个产品族,需要一个车间,就是一个XXXFactory,所以:

螺丝_Factory{
create_10号螺丝();
create_12号螺丝();
create_20号螺丝();
}

铁板_Factory{
create_10号铁板();
create_12号铁板();
create_20号铁板();
}

门把手_Factory{
create_10号门把手();
create_12号门把手();
create_20号门把手();
}

  • 增加一个零件,就是增加一个产品族:

    • 需要多一个AbstractXX类
    • 需要多M个产品等级类,都继承AbstractXXX类
    • 需要多一个XXFactory类,作为工厂,生产M个产品等级的产品
    • 本质上就是多了一条产品线
  • 增加一个产品等级,如30号门把手

    • 需要多一个产品等级类,继承Abstract门把手类
    • 门把手_Factory需要增加一个方法

4.1 简单工厂模式

  • 场景:
    • 一开始女娲造人不分男女,只分了个肤色,
    • 因为产品族太简单了,所以可以用简单工厂模式:
    • 一个工厂搞定所有产品
    • 这其实是工厂方法的弱化,即工厂不需要任何扩展,使用静态就行
//人类的统一接口
public interface IHuman {
    void say();
    void getColor();
}

//实现类:黑人
public class BlackHuman implements IHuman{

    @Override
    public void say() {
        System.out.println("我是黑种人");
    }

    @Override
    public void getColor() {
        System.out.println("黑色皮肤");
    }

}

//实现类:黄人
public class YellowHuman implements IHuman{

    @Override
    public void say() {
        System.out.println("我是黄种人");
    }

    @Override
    public void getColor() {
        System.out.println("黄色皮肤");
    }

}

//白人略过...

//此时女娲造人,需要依赖于具体实现类
public static void main(String[] args) {

    //===造白人
    IHuman wh = new WhiteHuman();
    wh.getColor();
    wh.say();

    //===造黑人
    IHuman bh = new BlackHuman();
    bh.getColor();
    bh.say();
}


//加上个简单工厂:
public class HumanFactory{

    public static <T extends IHuman> T createHuman(Class<T> clazz) {
        IHuman human = null;
        try{
            human = clazz.newInstance();
        }catch(Exception e){
            e.printStackTrace();
            System.out.println("生成错误");
        }
        return (T)human;
    }

}

//现在女娲是这样工作:
public static void main(String[] args) {

    //===造白人
    IHuman wh = HumanFactory.createHuman(WhiteHuman.class);
    wh.getColor();
    wh.say();

    //===造黑人
    IHuman bh = HumanFactory.createHuman(BlackHuman.class);
    bh.getColor();
    bh.say();

    //===造黄人
    IHuman yh = HumanFactory.createHuman(YellowHuman.class);
    yh.getColor();
    yh.say();
}
  • 分析
    • 大体一看,这样的代码基本没什么快感
    • 只不过工厂引入了一层封装,可以在不改变女娲端代码的情况下,控制一下各色人种的数量等
    • 注意,这里工厂的createHuman方法如果不想用反射,就可以考虑Builder或者Provider,如第一章中提到的。
      • 不管Builderr还是Provider,都得有3个实现类,代码变多了
    • 所以原则还是那个原则,简单就是美,关系简单,使用简单,代码简单,自己看情况取个折衷吧

4.2 抽象工厂:单工厂

代码还是类似简单工厂,因为产品体系还是很简单,就是给工厂多了个接口

public abstract class AbstractHumanFactory {
    public abstract <T extends IHuman> T createHuman(Class<T> clazz);
}

public class HumanFactory extends AbstractHumanFactory{

    @Override
    public <T extends IHuman> T createHuman(Class<T> clazz) {
        IHuman human = null;
        try{
            human = clazz.newInstance();
        }catch(Exception e){
            e.printStackTrace();
            System.out.println("生成错误");
        }
        return (T)human;
    }

}

女娲端的代码如下:
public static void main(String[] args) {
    AbstractHumanFactory factory = new HumanFactory();

    //===造白人
    IHuman wh = factory.createHuman(WhiteHuman.class);
    wh.getColor();
    wh.say();

    //===造黑人
    IHuman bh = factory.createHuman(BlackHuman.class);
    bh.getColor();
    bh.say();

    //===造黄人
    IHuman yh = factory.createHuman(YellowHuman.class);
    yh.getColor();
    yh.say();
}

4.3 抽象工厂:多工厂


///===============定义产品族:男女===================///
public class AbstractMan implements IHuman{

}

public class AbstractWomen implements IHuman{

}

///===============定义产品等级:以男人为例=============///
public YellowMan extends AbstractMan{

}


///===============定义抽象工厂,每个产品族对应一个工厂,所以男人一个,女人一个===///
public abstract class AbstractHumanFactory {
    public abstract IHuman createYellow();
    public abstract IHuman createBlack();
    public abstract IHuman createWhite();
}

public ManFactory extends AbstractHumanFactory{

}

public WomanFactory extends AbstractHumanFactory{

}

5 对象池:对象太重

6 Flyweight享元模式:对象太多

7 备忘录模式:状态保存,对象快照

8 原型模式:是啥来着

9 对象clone

10 序列化

11 四种引用

12 内存模型和垃圾回收

13 对象缓存

你可能感兴趣的:(java,android)