原創(chuàng)文章轉(zhuǎn)載請注明出處
最近受《IF YOU'RE SUBCLASSING, YOU'RE DOING IT WRONG》的影響,思考自己的代碼中是否有類似的場景,可以將父類的方法抽象出來,以面向協(xié)議的編程(protocol oriented programming既POP)方式來思考問題。
文中提到了傳統(tǒng)的OOP會帶來的各種問題,其中就有本文中要優(yōu)化的上帝類(God classes - 承擔(dān)著很多subclasses需要的重要高層級代碼的所有責(zé)任),Blobs(有過多職權(quán)的classes),Lava Flow(因為含有太多的非法代碼導(dǎo)致任何人都不敢碰的classes)等等這些種反面模式(anti patterns)。
ProgressHUD是每個應(yīng)用都會使用的控件,在GitHub上搜索HUD,至少可以找到100種以上相似的Swift/Objective-C開源控件,他們功能類似,只是展現(xiàn)效果不同。starts比較高的有:MBProgressHUD
、SVProgressHUD
、JGProgressHUD
、MMProgressHUD
等。
1.0版本
自11年底開始從事iOS開發(fā)以來,類似的控件嘗試過很多,最早是MBProgressHUD
,后來感覺其接口封裝方式不好,使用時不是很方便,又陸續(xù)嘗試了許多同類型的控件。每次進(jìn)行替換的時候,就要將散落在各處的調(diào)用方法統(tǒng)一進(jìn)行替換,雖然IDE提供的replace功能很方便,但是最后我還是將接口進(jìn)行了統(tǒng)一抽象,梳理出常用的方法,最后放到了項目中的BaseViewController中。
//類似Android toast的純文本提示框
func showTextOnlyView(message: String!) {
...
}
//等待框
func showWaitingView(message: String?) {
...
}
//隱藏等待框
func hideWaitingView() {
...
}
//n秒后隱藏等待框,可傳入回調(diào)函數(shù)
func hideWaitingViewDelay(interval: Double = 0.3, completion: ((Void) -> Void)? = nil ) {
...
}
//成功提示框,可傳入回調(diào)函數(shù)
func hideWaitingViewSuccess(message: String? = nil, completion: ((Void) -> Void)? = nil) {
...
}
//失敗提示框,可傳入回調(diào)函數(shù)
func hideWaitingViewError(message: String? = nil, completion: ((Void) -> Void)? = nil) {
...
}
上述方法基本上滿足了我在App中的使用需求,只要是繼承自BaseViewController的UIViewController,都可以直接調(diào)用以上方法,無需import第三方庫。而當(dāng)我哪天又看上某個漂亮的第三方庫的時候,我只需要修改基類中的方法,無需對全工程的代碼進(jìn)行替換。
問題來了,如果UIViewController不是繼承自我自定義的BaseViewController呢?如果我想在UIView中使用這些方法呢?聰明如我想到了將代碼抽離出來,放到獨立的類中,又或者對第三方庫做一個擴(kuò)展。
2.0版本
將上述方法封裝到一個獨立的類和直接對第三方庫做擴(kuò)展有什么區(qū)別嗎?區(qū)別大概就是做擴(kuò)展的話,在我調(diào)用方法的類中需要import對應(yīng)的庫,而我實在不想在替換第三方庫的時候還要滿世界去修改import代碼,那我們就選擇封裝一個ProgressHUD的類吧。
import SomeProgressHUD
class ProgressHUD {
class func showTextOnlyView(message: String!) {
...
}
class func showWaitingView(message: String?) {
...
}
class func hideWaitingView() {
...
}
class func hideWaitingViewDelay(interval: Double = 0.3, completion: ((Void) -> Void)? = nil ) {
...
}
class func hideWaitingViewSuccess(message: String? = nil, completion: ((Void) -> Void)? = nil) {
...
}
class func hideWaitingViewError(message: String? = nil, completion: ((Void) -> Void)? = nil) {
...
}
}
現(xiàn)在我們可以隨便使用了,只需要import ProgressHUD,就可以通過ProgressHUD.xxx
這樣的形式進(jìn)行調(diào)用。同樣的,當(dāng)我哪天又看上某個漂亮的第三方庫的時候,我只需要修改ProgressHUD
類中的實現(xiàn),無需對全工程的代碼進(jìn)行替換。
從封裝的角度來講,現(xiàn)在這個版本已經(jīng)找不出什么太大的問題了。通過封裝類適配了第三方庫,代碼中對于使用哪個庫并不關(guān)心,方便了代碼維護(hù)。
前文說了,最近受《IF YOU'RE SUBCLASSING, YOU'RE DOING IT WRONG》的影響,心里又長草了,因為不喜歡每次調(diào)用接口都要加ProgressHUD.
這樣的前綴,懷疑自己不是雙子座??而是處女座??。
3.0版本
好吧,既然這樣不如就嘗試一下協(xié)議擴(kuò)展。
import Foundation
import SomeProgressHUD
// MARK: - ProgressHUDable
protocol ProgressHUDable {
func showTextOnlyView(message: String!)
func showWaitingView(message: String?)
func hideWaitingView()
func hideWaitingViewDelay(interval: Double, completion: ((Void) -> Void)?)
func hideWaitingViewSuccess(message: String?, completion: ((Void) -> Void)?)
func hideWaitingViewError(message: String?, completion: ((Void) -> Void)?)
}
extension ProgressHUDable where Self: UIViewController {
func showTextOnlyView(message: String!) {
...
}
func showWaitingView(message: String?) {
...
}
func hideWaitingView() {
...
}
func hideWaitingViewDelay(interval: Double = 0.3, completion: ((Void) -> Void)? = nil ) {
...
}
func hideWaitingViewSuccess(message: String? = nil, completion: ((Void) -> Void)? = nil) {
...
}
func hideWaitingViewError(message: String? = nil, completion: ((Void) -> Void)? = nil) {
...
}
}
extension ProgressHUDable where Self: UIView {
...
}
需要使用的類就帶上該協(xié)議
class LoginViewController: UIViewController, ProgressHUDable {
...
}
終于可以擺脫前綴了,感覺世界清靜了。附上我現(xiàn)在使用的代碼,沒錯我最近看上的是KVNProgress
。Async
是對GCD封裝的語法糖,使用起來很方便,順便推薦一下。
//
// ProgressHUDable.swift
// MyProject
//
// Created by chenjsa on 16/3/1.
// Copyright ? 2016年 MyCompany. All rights reserved.
//
import Foundation
import KVNProgress
import Async
// MARK: - ProgressHUDable
protocol ProgressHUDable {
func showTextOnlyView(message: String!)
func showWaitingView(message: String?)
func hideWaitingView()
func hideWaitingViewDelay(interval: Double, completion: ((Void) -> Void)?)
func hideWaitingViewSuccess(message: String?, completion: ((Void) -> Void)?)
func hideWaitingViewError(message: String?, completion: ((Void) -> Void)?)
}
extension ProgressHUDable where Self: UIViewController {
func showTextOnlyView(message: String!) {
KVNProgress.configuration().circleSize = 0
KVNProgress.showErrorWithStatus(message)
}
func showWaitingView(message: String?) {
var msg = message
if ValidatorUtils.isEmptyString(msg) {
msg = NSLocalizedString("wait", comment: "")
}
KVNProgress.configuration().circleSize = 80
KVNProgress.showWithStatus(msg)
}
func hideWaitingView() {
KVNProgress.dismiss()
}
func hideWaitingViewDelay(interval: Double = 0.3, completion: ((Void) -> Void)? = nil ) {
Async.main(after: interval) { _ in
KVNProgress.dismissWithCompletion(completion)
}
}
func hideWaitingViewSuccess(message: String? = nil, completion: ((Void) -> Void)? = nil) {
var msg = message
if ValidatorUtils.isEmptyString(msg) {
msg = NSLocalizedString("success", comment: "")
}
KVNProgress.configuration().circleSize = 80
KVNProgress.showSuccessWithStatus(msg, completion: completion)
}
func hideWaitingViewError(message: String? = nil, completion: ((Void) -> Void)? = nil) {
var msg = message
if ValidatorUtils.isEmptyString(msg) {
msg = NSLocalizedString("error", comment: "")
}
KVNProgress.configuration().circleSize = 80
KVNProgress.showErrorWithStatus(msg, completion: completion)
}
}
extension ProgressHUDable where Self: UIView {
func showTextOnlyView(message: String!) {
KVNProgress.configuration().circleSize = 0
KVNProgress.showErrorWithStatus(message)
}
func showWaitingView(message: String?) {
var msg = message
if ValidatorUtils.isEmptyString(msg) {
msg = NSLocalizedString("wait", comment: "")
}
KVNProgress.configuration().circleSize = 80
KVNProgress.showWithStatus(msg)
}
func hideWaitingView() {
KVNProgress.dismiss()
}
func hideWaitingViewDelay(interval: Double = 0.3, completion: ((Void) -> Void)? = nil ) {
Async.main(after: interval) { _ in
KVNProgress.dismissWithCompletion(completion)
}
}
func hideWaitingViewSuccess(message: String? = nil, completion: ((Void) -> Void)? = nil) {
var msg = message
if ValidatorUtils.isEmptyString(msg) {
msg = NSLocalizedString("success", comment: "")
}
KVNProgress.configuration().circleSize = 80
KVNProgress.showSuccessWithStatus(msg, completion: completion)
}
func hideWaitingViewError(message: String? = nil, completion: ((Void) -> Void)? = nil) {
var msg = message
if ValidatorUtils.isEmptyString(msg) {
msg = NSLocalizedString("error", comment: "")
}
KVNProgress.configuration().circleSize = 80
KVNProgress.showErrorWithStatus(msg, completion: completion)
}
}
我是咕咕雞,一個還在不停學(xué)習(xí)的全棧工程師。
熱愛生活,喜歡跑步,家庭是我不斷向前進(jìn)步的動力。