Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 安卓簡陋手冊系列1:對象管理

安卓簡陋手冊系列1:對象管理

編輯:關於Android編程

說是原創,但本系列實際上是編程思想,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 providers = new ConcurrentHashMap<>();
    public static final String DEFAULT_PROVIDER_NAME = "";

    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 {

    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{

        //必填的參數,無默認值
        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 builder = new NutritionFacts2.MyBuilder(240,  8)
                .calories(100)
                .sodium(35)
                .carbohydrate(27);

        NutritionFacts2 cocacola = builder.build();
    }
}

/*
這裡的Builder 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 createHuman(Class 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 createHuman(Class clazz);
}

public class HumanFactory extends AbstractHumanFactory{

    @Override
    public  T createHuman(Class 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 對象緩存

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved