一记焊、優(yōu)化代碼第一步——單一職責(zé)原則
- 一個類中應(yīng)該是一組相關(guān)性很高的函數(shù)弥喉、數(shù)據(jù)的封裝
- 合理的劃分一個類冈爹、一個函數(shù)的職責(zé)。例如:完全不一樣的功能就不能放在同一類中、
- 需要不斷審視自己的代碼另伍,根據(jù)具體業(yè)務(wù)功能進行拆分
具體案例:
結(jié)構(gòu)清晰壹甥,兩個類得功能邏輯互相獨立不會影響彼此
ImageLoader
負責(zé)圖片加載的邏輯
public class ImageLoader {
//圖片緩存
ImageCache mImageCache = new ImageCache();
//線程池空幻,線程數(shù)量為CPU的數(shù)量
ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
Handler mUiHandler = new android.os.Handler(Looper.getMainLooper());
private void updateImageView(final ImageView imageView, final Bitmap bitmap) {
mUiHandler.post(new Runnable() {
@Override
public void run() {
imageView.setImageBitmap(bitmap);
}
});
}
//加載圖片
public void displayImage(final String url, final ImageView imageView) {
Bitmap bitmap = mImageCache.get(url);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return;
}
imageView.setTag(url);
mExecutorService.submit(new Runnable() {
@Override
public void run() {
Bitmap bitmapD = downloadImage(url);
if (bitmapD == null) return;
if (imageView.getTag().equals(url)) {
updateImageView(imageView, bitmap);
}
mImageCache.put(url, bitmap);
}
});
}
public Bitmap downloadImage(String imageUrl) {
Bitmap bitmap = null;
try {
URL url = new URL(imageUrl);
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
bitmap = BitmapFactory.decodeStream(connection.getInputStream());
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
}
ImageCache
負責(zé)處理圖片緩存的邏輯
public class ImageCache {
//圖片LRU緩存
LruCache<String, Bitmap> mImageCache;
public ImageCache() {
}
private void initImageCache() {
//計算可使用的最大內(nèi)存
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
//取四分之一的可用內(nèi)存作為緩存
final int cacheSize = maxMemory / 4;
mImageCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight() / 1024;
}
};
}
public void put(String url, Bitmap bitmap) {
mImageCache.put(url, bitmap);
}
public Bitmap get(String url) {
return mImageCache.get(url);
}
}
二熙兔、讓程序更穩(wěn)定、更靈活——開閉原則
開閉原則:Open Close Principle挽懦,縮寫OCP
**定義:**軟件中的對象(類翰意、模塊、函數(shù)等)應(yīng)該對于擴展是開放的,但是冀偶,對于修改是封閉的醒第。
當(dāng)軟件需要變化時,應(yīng)該盡量通過擴展的方式來實現(xiàn)變化进鸠,而不是通過修改已有的代碼來實現(xiàn)
具體案例:
實現(xiàn)ImageCache
接口實現(xiàn)不同的緩存類稠曼,通過setImageCache
方法注入不同的緩存,使ImageLoader
更簡單客年、健壯霞幅,也使得ImageLoader
可擴展性、靈活性更高
ImageLoader
public class ImageLoader {
//圖片緩存
ImageCache mImageCache = new MemoryCache();
//線程池量瓜,線程數(shù)量為CPU的數(shù)量
ExecutorService mExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
Handler mUiHandler = new android.os.Handler(Looper.getMainLooper());
private void updateImageView(final ImageView imageView, final Bitmap bitmap) {
mUiHandler.post(new Runnable() {
@Override
public void run() {
imageView.setImageBitmap(bitmap);
}
});
}
//注入緩存實現(xiàn)
public void setImageCache(ImageCache cache){
mImageCache=cache;
}
//加載圖片
public void displayImage(final String url, final ImageView imageView) {
Bitmap bitmap = mImageCache.get(url);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
return;
}
//圖片沒緩存蝗岖,提交到線程池中下載圖片
submitLoadRequest(url,imageView);
}
private void submitLoadRequest(final String imageUrl,final ImageView imageView){
imageView.setTag(imageUrl);
mExecutorService.submit(new Runnable() {
@Override
public void run() {
Bitmap bitmap = downloadImage(imageUrl);
if (bitmap == null) return;
if (imageView.getTag().equals(imageUrl)) {
updateImageView(imageView, bitmap);
}
mImageCache.put(imageUrl, bitmap);
}
});
}
public Bitmap downloadImage(String imageUrl) {
Bitmap bitmap = null;
try {
URL url = new URL(imageUrl);
final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
bitmap = BitmapFactory.decodeStream(connection.getInputStream());
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
}
ImageCache
接口,用來抽象圖片緩存功能
public interface ImageCache {
public Bitmap get(String url);
public void put(String url,Bitmap bitmap);
}
MemoryCache
:內(nèi)存緩存類
public class MemoryCache implements ImageCache{
private LruCache<String, Bitmap> mMemoryCache;
public MemoryCache() {
//初始化LRU緩存
initMemoryCache();
}
private void initMemoryCache() {
//計算可使用的最大內(nèi)存
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
//取四分之一的可用內(nèi)存作為緩存
final int cacheSize = maxMemory / 4;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight() / 1024;
}
};
}
@Override
public Bitmap get(String url) {
return mMemoryCache.get(url);
}
@Override
public void put(String url, Bitmap bitmap) {
mMemoryCache.put(url, bitmap);
}
}
DiskCache
:將圖片緩存到SD卡
public class DiskCache implements ImageCache{
static String cacheDir="sdcard/cache/";
//從緩存中獲取圖片
@Override
public Bitmap get(String url){
return BitmapFactory.decodeFile(cacheDir+url);
}
//將圖片緩存到內(nèi)存中
@Override
public void put(String url,Bitmap bitmap){
FileOutputStream fileOutputStream=null;
try {
fileOutputStream=new FileOutputStream(cacheDir+url);
bitmap.compress(Bitmap.CompressFormat.PNG,100,fileOutputStream);
}catch (FileNotFoundException e){
e.printStackTrace();
}finally{
if(fileOutputStream!=null){
try {
fileOutputStream.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
}
DoubleCache
:雙緩存類
獲取圖片時先從內(nèi)存緩存中獲取榔至,如果內(nèi)存中沒有緩存該圖片,再從SD卡中獲取欺劳。緩存圖片也是在內(nèi)存和SD卡中都緩存一份
public class DoubleCache implements ImageCache{
ImageCache mMemoryCache=new MemoryCache();
DiskCache mDiskCache=new DiskCache();
//先從內(nèi)存緩存中獲取圖片唧取,如果沒有,再從SD卡中獲取
@Override
public Bitmap get(String url){
Bitmap bitmap=mMemoryCache.get(url);
if(bitmap==null) bitmap=mDiskCache.get(url);
return bitmap;
}
//將圖片緩存到內(nèi)存和SD卡中
@Override
public void put(String url,Bitmap bitmap){
mMemoryCache.put(url,bitmap);
mDiskCache.put(url,bitmap);
}
}
用戶通過ImageLoader
類中的setImageCache
函數(shù)設(shè)置緩存實現(xiàn)划提,也就是通常說的依賴注入
ImageLoader imageLoader=new ImageLoader();
//使用內(nèi)存緩存
imageLoader.setImageCache(new MemoryCache());
//使用SD卡緩存
imageLoader.setImageCache(new DiskCache());
//使用雙緩存
imageLoader.setImageCache(new DoubleCache());
//使用自定義的圖片緩存
imageLoader.setImageCache(new ImageCache() {
@Override
public Bitmap get(String url) {
return null;//從緩存中獲取圖片
}
@Override
public void put(String url, Bitmap bitmap) {
//緩存圖片
}
});
三枫弟、構(gòu)建擴展性更好的系統(tǒng)——里氏替換原則
里氏替換原則:Liskov Substitution Principle,縮寫LSP
第一種定義:如果對每一個類型為S的對象O1鹏往,都有類型為T的對象O2淡诗,使得以T定義的所有程序P在所有的對象O1都替換成O2時,程序P的行為沒有發(fā)生變化伊履,那么類型S時類型T的子類型韩容。
第二種定義:所有引用基類的地方必須能透明地使用其子類的對象
核心原則:抽象,抽象又依賴于繼承這個特性
-
在OOP中唐瀑,繼承的優(yōu)缺點
優(yōu)點
- 代碼重寫群凶,減少創(chuàng)建類的成本,每個子類都擁有父類中的方法和屬性
- 子類與父類基本相似哄辣,但又與父類有所區(qū)別
- 提高代碼的可擴展性
缺點
- 繼承是侵入性的请梢,只要繼承就必須擁有父類的所有屬性和方法
- 可能造成子類代碼冗余、靈活性降低
具體案例:
- Window依賴于View
- View定義一個視圖抽象力穗,measure是各個子類共享的方法
- 子類通過覆寫View的draw方法實現(xiàn)具有各自特色的功能
- 任何繼承自View的子類都可以傳遞給show函數(shù)毅弧,這就是里氏替換
- Window負責(zé)組織View,并且將View顯示到屏幕上
//窗口類
public class Window{
public void show(View child){
child.draw();
}
}
//建立視圖抽象当窗,測量視圖的寬高為公共代碼够坐,繪制時先交給具體的子類
public abstract class View{
public abstract void draw();
public void measure(int width,int height){
//測量視圖大小
}
}
//文本控制類的具體實現(xiàn)
public class TextView extends View{
@Override
public void draw() {
//繪制本文
}
}
//ImageView的具體實現(xiàn)
public class ImageView extends View{
@Override
public void draw() {
//繪制圖片
}
}
四、讓項目擁有變化的能力——依賴倒置原則
依賴倒置原則:Dependence Inversion Principle,縮寫是DIP
指一種特定的解耦形式咆霜,使得高層次的模塊不依賴于低層次模塊的實現(xiàn)細節(jié)的目的
關(guān)鍵:
- 高層模塊不應(yīng)該依賴低層模塊邓馒,兩者都應(yīng)該依賴其抽象
- 抽象不應(yīng)該依賴細節(jié)
- 細節(jié)應(yīng)該依賴抽象
**面向抽象編程:**抽象指的是接口或者抽象類
**在Java語言中的表現(xiàn):**模塊間的依賴通過抽象發(fā)生,實現(xiàn)類之間不發(fā)生直接的依賴關(guān)系蛾坯,其依賴關(guān)系是通過接口或抽象類產(chǎn)生的光酣。
具體案例:
建立ImageCache抽象,讓ImageLoader依賴于抽象而不是具體細節(jié)脉课。當(dāng)需求發(fā)生變化時救军,只需要實現(xiàn)ImageCache類或者繼承其他已有的ImageCache子類完成相應(yīng)的緩存功能,然后將具體的實現(xiàn)注入到ImageLoader即可實現(xiàn)緩存功能的替換
public interface ImageCache {
public Bitmap get(String url);
public void put(String url,Bitmap bitmap);
}
public class ImageLoader {
//圖片緩存類倘零,依賴于抽象唱遭,并且有一個默認的實現(xiàn)
ImageCache mImageCache = new MemoryCache();
//加載圖片
public void displayImage(final String url, final ImageView imageView) {
Bitmap bitmap = mImageCache.get(url);
if (bitmap == null) {
//異步加載圖片
downloadImageAsync(url,imageViewl);
}else{
imageView.setImageBitmap(bitmap);
}
}
//設(shè)置緩存策略,依賴于抽象
public void setImageCache(ImageCache cache){
mImageCache=cache;
}
/.../
}
五呈驶、系統(tǒng)有更高的靈活性——接口隔離原則
接口隔離原則:Interface Segregation Principles拷泽,縮寫是ISP
定義:
- 客戶端不應(yīng)該依賴他不需要的接口
- 類間的依賴關(guān)系應(yīng)該建立在最小的接口上
將非常龐大臃腫的接口拆分成更小的和更具體的接口
**目的:**系統(tǒng)揭開耦合,從而容易重構(gòu)袖瞻、更改和重新部署
具體案例:
closeQuietLly方法的基本原理是依賴于Closeable抽象而不是具體實現(xiàn)司致,并且建立在最小化依賴原則的基礎(chǔ)上,它只需要知道這個對象時可關(guān)閉的聋迎,不用去關(guān)心別的脂矫,即接口隔離原則。
public class CloseUtils {
private CloseUtils() {}
/**
* 關(guān)閉Closeable對象
* @param closeable
*/
public static void closeQuietly(Closeable closeable){
if(null!=closeable){
try{
closeable.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
public void put(String url, Bitmap bitmap){
FileOutputStream fileOutputStream=null;
try {
fileOutputStream=new FileOutputStream(cacheDir+url);
bitmap.compress(Bitmap.CompressFormat.PNG,100,fileOutputStream);
}catch (FileNotFoundException e){
e.printStackTrace();
}finally {
CloseUtils.closeQuietly(fileOutputStream);
}
}
六霉晕、更好的可拓展性——迪米特原則
迪米特原則:Law of Demeter庭再,縮寫是LOD,也稱為最少知識原則(Least Knowledge Principle)
一個類有個對自己需要耦合或調(diào)用的類知道得最少牺堰,類的內(nèi)部如何實現(xiàn)與調(diào)用者或者依賴者沒關(guān)系拄轻,調(diào)用者或者依賴者只需要知道它需要的方法即可
Only talk to your immediate friend “只與直接的朋友通信”
wg:類與類之間的關(guān)聯(lián)盡可能分開,避免多個類耦合在一起伟葫,每個類的職責(zé)要明確
舉例:
租戶提供所需的房間面積和價格給中介哺眯,中介將符合要求的房子提供給租戶
錯誤示范:
Tenant不僅依賴了Mediator類,還需要借助Room類扒俯,這樣使得中介類的功能弱化不夠明確奶卓,也使得Tenant與Room的耦合較高。
/**
* 房間
*/
public class Room{
public float area;
public float price;
public Room(float area, float price) {
this.area = area;
this.price = price;
}
@Override
public String toString() {
return "Room{" +
"area=" + area +
", price=" + price +
'}';
}
}
/**
* 中介
*/
public class Mediator{
List<Room> mRooms=new ArrayList<>();
public Mediator() {
for(int i=0;i<5;i++){
mRooms.add(new Room(14+i,(14+i)*150));
}
}
public List<Room> getAllRooms() {
return mRooms;
}
}
/**
* 租客
*/
public class Tenant{
public void rentRoom(float roomArea,float roomPrice,Mediator mediator){
List<Room> rooms=mediator.getAllRooms();
for(Room room:rooms){
if(isSuitable(roomArea,roomPrice,room)){
System.out.println("租到房子了撼玄!"+room);
break;
}
}
}
//租金要小于等于指定的值夺姑,面積要大于等于指定的值
private boolean isSuitable(float roomArea,float roomPrice,Room room){
return room.price<=roomPrice&&room.area>=roomArea;
}
}
正確示范:
將Room相關(guān)的操作從Tenant移入Mediator類
/**
* 中介
*/
public class Mediator{
List<Room> mRooms=new ArrayList<>();
public Mediator() {
for(int i=0;i<5;i++){
mRooms.add(new Room(14+i,(14+i)*150));
}
}
public Room rentOut(float area,float price){
for(Room room:mRooms){
if(isSuitable(area,price,room)){
return room;
}
}
return null;
}
private boolean isSuitable(float roomArea,float roomPrice,Room room){
return room.price<=roomPrice&&room.area>=roomArea;
}
}
/**
* 租客
*/
public class Tenant{
public void rentRoom(float roomArea,float roomPrice,Mediator mediator){
System.out.println("租到房子了!"+mediator.rentOut(roomArea,roomPrice));
}
}