編輯:關於Android編程
這篇我們來介紹一下享元模式(Flyweight Pattern),Flyweight 代表輕量級的意思,享元模式是對象池的一種實現。享元模式用來盡可能減少內存使用量,它適合用於可能存在大量重復對象的場景,緩存可共享的對象,來達到對象共享和避免創建過多對象的效果,這樣一來就可以提升性能,避免內存移除和頻繁 GC 等。
享元模式的一個經典使用案例是文本系統中圖形顯示所用的數據結構,一個文本系統能夠顯示的字符種類就是那麼幾十上百個,那麼就定義這麼些基礎字符對象,存儲每個字符的顯示外形和其他的格式化數據等,而不用每次都去新建對象,這樣就可以避免創建成千上萬的重復對象,大大提高對象的重用率。
使用共享對象可有效地支持大量細粒度的對象。
共享模式支持大量細粒度對象的復用,所以享元模式要求能夠共享的對象必須是細粒度對象。在了解享元模式之前我們先要了解兩個概念:內部狀態、外部狀態:
看了上面的特征會讓我們想到對象池模式,確實,對象池模式和享元模式有很多的相同點,但是他們有一個很重要的不同點:享元模式通常情況下獲取的是不可變的實例,而從對象池模式中獲取的對象通常情況下是可變的。所以使用享元模式用來避免創建多個擁有同樣狀態的對象,只創建一個並且在應用的不同地方都使用這一個實例;而對象池中的資源從使用的角度上看具有不同的狀態並且每個都需要單獨控制,但是又不想花費一定的資源區頻繁的創建和銷毀這些資源對象,畢竟他們都有相同的初始化過程。
簡而言之,享元模式更加傾向於狀態的不可變性,而對象池模式則是狀態的可變性。
享元模式的 uml 類圖如下:
public interface Flyweight {
void operation();
}
ConcreteFlyweight.class
public class ConcreteFlyweight implements Flyweight{
private String intrinsicState;
public ConcreteFlyweight(String state) {
intrinsicState = state;
}
@Override
public void operation() {
Log.e("Shawn", "ConcreteFlyweight----" + intrinsicState);
}
}
FlyweightFactory.class
public class FlyweightFactory {
private HashMap mFlyweights = new HashMap<>();
public Flyweight getFlyweight(String key) {
Flyweight flyweight = mFlyweights.get(key);
if (flyweight == null) {
flyweight = new ConcreteFlyweight(key);
mFlyweights.put(key, flyweight);
}
return flyweight;
}
}
測試代碼
Flyweight flyweight1 = factory.getFlyweight("a");
Flyweight flyweight2 = factory.getFlyweight("b");
Flyweight flyweight3 = factory.getFlyweight("a");
Log.e("Shawn", "flyweight1==flyweight2 : " + (flyweight1 == flyweight2));
Log.e("Shawn", "flyweight1==flyweight3 : " + (flyweight1 == flyweight3));
break;
結果
com.android.flyweightpattern E/Shawn: flyweight1==flyweight2 : false
com.android.flyweightpattern E/Shawn: flyweight1==flyweight3 : true
可以很明顯的看出 flyweight1 和 flyweight3 對象是同樣一個享元對象。
在 Java 中,最經典使用享元模式的案例就應該是 String 了,String 存在常量池中,也就是說一個 String 被定義之後它就被緩存到了常量池中,當其他地方要使用同樣的字符串時,則直接使用該緩存,而不會重復創建(這也就是 String 的不可變性:java/android 設計模式學習筆記(11)—原型模式)。
比如下面的代碼:
String str1 = new String("abc");
String str2 = new String("abc");
String str3 = "abc";
String str4 = "ab" + "c";
str1 == str2; //false
str3 == str4; //true
str1 和 str2 是兩個不同的對象,這個應該顯而易見,而 str3 和 str4 由於都是使用的 String 享元池,所以他們兩個是同一個對象。
我們這以一個圖形系統為例,用來畫不同顏色的圓形:
Shape.class用來定義一個圖形的基本行為:
public interface Shape {
void draw();
}
Circle.class Shape 的實現子類,用來畫圓形:
public class Circle implements Shape{
String color;
public Circle(String color) {
this.color = color;
}
@Override
public void draw() {
Log.e("Shawn", "畫了一個" + color +"的圓形");
}
}
ShapeFactory.class 圖形享元工廠類:
public class ShapeFactory {
private HashMap shapes = new HashMap<>();
public Shape getShape(String color) {
Shape shape = shapes.get(color);
if (shape == null) {
shape = new Circle(color);
shapes.put(color, shape);
}
return shape;
}
public int getSize() {
return shapes.size();
}
}
測試代碼
Shape shape1 = factory.getShape("紅色");
shape1.draw();
Shape shape2 = factory.getShape("灰色");
shape2.draw();
Shape shape3 = factory.getShape("綠色");
shape3.draw();
Shape shape4 = factory.getShape("紅色");
shape4.draw();
Shape shape5 = factory.getShape("灰色");
shape5.draw();
Shape shape6 = factory.getShape("灰色");
shape6.draw();
Log.e("Shawn", "一共繪制了"+factory.getSize()+"中顏色的圓形");
最後運行結果
從結果可以看到,同一個顏色的圖形共用一個對象,總共只創建了 3 個對象。
享元模式實現比較簡單,但是它的作用在某些場景確實極其重要。它可以大大減少應用程序創建對象的數量和頻率,降低程序內存的占用,增強程序的性能,但它同時也增加了系統的復雜性,需要分離出外部狀態和內部狀態,內部狀態為不變的共享部分,存儲於享元對象內部;而外部狀態具有固化特性,應當由客戶端來負責,不應該隨著內部狀態改變而改變,否則會導致系統的邏輯混亂。
享元模式優點:
我在查閱相關書籍和網絡資料的過程中,看到有些文章會把 Android 中的 MessagePool 定義為享元模式,但是對比了對象池模式和享元模式之後,我更傾向於認為它是對象池模式,因為從上面介紹的對比來看,MessagePool 中對象池有初始化的 size,每次從 MessagePool 中去 obtain Message 對象的時候,獲取的都是一個初始對象,其中的狀態都需要去根據需求變化,而享元模式則更傾向於重用具有相同狀態的對象,這個對象著重於在應用的每個使用地方它的狀態都具有相同性,從這個原則來看就已經排除是享元模式了,不過還是個人的看法,有沒有大神指導一下,不勝感激~~~~
https://github.com/zhaozepeng/Design-Patterns/tree/master/FlyweightPattern
最近很多人在玩一個《貪吃蛇大作戰》的游戲,以前小時候也經常在文曲星上玩貪吃蛇這個小游戲,於是自己就試著寫一個傳統的貪吃蛇游戲來玩玩,先寫了一個簡單demo。我們知道小蛇是
上文我們地完成了『啟動沒有在AndroidManifest.xml中顯式聲明的Activity』的任務;通過HookAMS和攔截ActivityThread中H類對於組件
本文會實現一個類似網易新聞(不說網易新聞大家可能不知道大概是什麼樣子)點擊超多選項卡,選項卡動態滑動的效果。首先來看看布局,就是用HorizontalScro
以前編程的時候,遇到倒計時的功能時,經常自己去寫,但其實Android已經幫封裝好了一個倒計時類CountDownTimer,其實是將後台線程的創建和Handler隊列封