如何优化一堆的if条件语句

如何优化一堆的if条件语句

1.前提

  func test() {
    let currentList = model
        for outlinesIndex in 0...currentList.outlines.count - 1 {
            for subOutlinesIndex in 0...currentList.outlines[outlinesIndex].subOutlines.count - 1 {
                if currentList.outlines[outlinesIndex].subOutlines[subOutlinesIndex].id == currentList.livingCardPackageContentId {
                    ///先定位至分区才能获取显示cell,否则因为cell重用无法获取到未显示的cell
                    self.tableView.scrollToRow(at: IndexPath(row: 0, section: outlinesIndex), at: .none, animated: true)
                    //bottomOffsetY <= 0 说明已经滑到最底部
                    let bottomOffsetY = self.tableView.contentSize.height - self.tableView.bounds.size.height
                    guard bottomOffsetY > 0 else {
                        return
                    }
                    
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
                        ///获取显示的cell
                        guard let cell = self.tableView.cellForRow(at: IndexPath(row: 0, section: outlinesIndex)) as? ServicesListTableViewCell else {
                            return
                        }
                        for view in cell.subviews {
                            if let subTableView = view.viewWithTag(888) as? UITableView {
                                guard let subCell = subTableView.cellForRow(at: IndexPath(row: subOutlinesIndex, section: 0)) as? ServicesSubListTableViewCell else {
                                    return
                                }
                                let livingCellPoint = self.tableView.convert(CGPoint(x: subCell.frame.origin.x, y: subCell.frame.origin.y), from: subCell.superview)
                                if livingCellPoint.y > bottomOffsetY {
                                    self.tableView.setContentOffset(CGPoint.init(x: 0, y: bottomOffsetY), animated: true)
                                } else {
                                    self.tableView.setContentOffset(CGPoint.init(x: 0, y: livingCellPoint.y - 8), animated: true)
                                }
                            }
                        }
                    }
                }
  }

我们先来看一段代码,类似这样的代码是否也曾在你们的项目中无处不见?

2. 透过本质看问题

本身来说if语句是没有问题的,这也就是为什么几乎所有的语言都有设计if条件语句。if条件语句也会让逻辑更清晰、扩展。但是, 如果像上边这样if嵌套、if里做的事情较多,一个方法可能有100+以上的代码。那么if语句可能就会变的不是太好维护和扩展。我们需要一些手段去简化(或者叫优化)if语句。

方案

3.1 卫语句

最简单方案莫过于卫语句,那么卫语句是什么什么?卫语句其实就是判断不符合的条件直接退出。比如:

func test() {
	if xxxx == nil {
		return
	}
  
  ...
}
// swift 推荐
func test() {
	guard xxxx != nil else {
		return
	}
  
  ...
}

善于使用卫语句,提前return,也在一定程度上简化if条件判断。但是首先我们的思路需要转变,我们需要先考虑不满足的条件。

3.2 三目运算优化

那其实三目运算也是一种好的优化方式(:我就比较喜欢用三目运算, 但是三目运算也只能是适合简单的代替。

比如:

func test() {
	var title = ""
	if type == .live {
		title = "直播"
	} else {
		title = "回放"
	}
}

// 我们其实用三目运算会更简单
func test() {
	let title = type == .live ? "直播" : "回放"
}
// 是不是看起来更简单。明了了呢?

3.3 合并条件

合并条件其实就是如果我们有处理相同事务的条件,可以进行合并判断。

比如:

func test() {
	if type == .live {
			jumpLiveOrPlayback()
	} else if == .playback {
			jumpLiveOrPlayback()
	} else if == .gaozhongXiaoBan {
			jumpLiveOrPlayback()
	} else if == .chuzhongXiaoBan {
			jumpClassin()
	} else if == .video {
			jumpVideo()
	}
}

// 优化后
func test() {
	if type == .live || type == .playback || type == .gaozhongXiaoBan {
				jumpLiveOrPlayback()
	} else if == .chuzhongXiaoBan {
			jumpClassin()
	} else if == .video {
			jumpVideo()
	}
}

合理的合并条件,在很大程度上能提高代码的可读性。

3.4 if 内容封装

什么叫if内容封装, 也就是说我们将if条件为真需要执行的代码,用方法封装起来。

比如:

func test() {
	if a == 1 {
		a()
	} else if a == 2 {
		b()
	} else if a == 3 {
		c()
	} else {
		d()
	}
}

func a() {
	...
} 

func b() {
	...
}

func c() {
	...
}

func d() {
	...
}

3.5 利用设计模式

3.5.1 策略模式

什么是策略模式呢?维基百科说:指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。从某种程度上说也是符合if条件语句的,条件语句则就是根据条件不同执行的逻辑不同。

怎么实现呢?

func test() {
	if type == 1 {
			doSomething1()
	} else if type == 2 {
			doSomething2()
	} else if type == 3 {
			doSomething3()
	}
}

// 优化后
open class AbstractsStrategy {
	func doSomething() {}
}

class Strategy1: AbstractsStrategy {
	override func doSomething() {
		...
	}
}

class Strategy2: AbstractsStrategy {
	override func doSomething() {
		...
	}
}

class Strategy3: AbstractsStrategy {
	override func doSomething() {
		...
	}
}

class StrategyFactory {
		private var strategy: AbstractStrategy?
		private var strategyMap = [1: Strategy1(), 2: Strategy2(), 3: Strategy3()]
		
		func doSomething(_ type: Int) {
			 strategy = strategyMap[type] 
			 guard let strategy = self.strategy else {
			 		return
			 }
			 
			 strategy.doSomething()
		}
}

func test() {
		StragtegyFactory().doSomething(type)
}
3.5.2 简单工厂模式

其实简单工厂模式和策略模式代码类似,区别在于,简单工厂模式侧重于产出product实例。策略模式则是侧重于行为。所以根据设计模式的分类我们称简单工厂模式为创建型模式,而策略模式为行为模式。

如:

func test() {
	if type == 1 {
			doSomething1()
	} else if type == 2 {
			doSomething2()
	} else if type == 3 {
			doSomething3()
	}
}

open class AbstractHandler {
		func doSomething()
}

class Handler1: AbstractHandler {
		func doSomething()
}

class Handler2: AbstractHandler {
		func doSomething()
}

class Handler3: AbstractHandler {
		func doSomething()
}

class Handler {
		private var strategyMap = [1: Handler1(), 2: Handler2(), 3: Handler3()]
		class func create(_ type) -> AbstractHandler? {
			return Map[type]
		}
}

func test() {
		Handler.create(type)?.doSomething()
}
3.5.3 责任链模式

责任链的定义为:每一个处理对象决定它能处理哪些命令对象,它也知道如何将它不能处理的命令对象传递给该链中的下一个处理对象。该模式还描述了往该处理链的末尾添加新的处理对象的方法(百度百科)。

如:

func test() {
	if type == 1 {
			doSomething1()
	} else if type == 2 {
			doSomething2()
	} else if type == 3 {
			doSomething3()
	}
}

open class AbstractHandler {
		var next: AbstractHandler?
		var type: Int = 0
		
		init(type: Int) {
				self.type = type
		}
		
		func setNext(_ next: AbstractHandler) {
				self.next = next
		}
		
		func execute(_ type: Int) {
				if type == self.type {
						do()
				} else if let next = self.next {
						next.doSomething()
				} else {
						print("没有处理器")
				}
		}
		
		func do() {}
}

class Handler1: AbstractHandler {
		init() {
			super.init(type: 1)
		}
		override func do() {
				...
		}
}

class Handler2: AbstractHandler {
		init() {
			super.init(type: 2)
		}
		override func do() {
				...
		}
}

class Handler3: AbstractHandler {
		init() {
			super.init(type: 3)
		}
		override func do() {
				...
		}
}

func test() {
		let handler1 = Handler1()
		let handler2 = Handler2()
		let handler3 = Handler3()
		
		handler1.setNext(handler2)
		handler2.setNext(handler3)
		
		handler1.execute(type)
}


最后看一下我们开头说的代码优化后的样子:

  func test() {        
        var destinationRow: Int? = nil
        let destinationSection = model.outlines.firstIndex { outline in
            destinationRow = outline.subOutlines.firstIndex(where: {$0.id == model.livingCardPackageContentId})
            return destinationRow != nil
        }
        
        guard let section = destinationSection, let row = destinationRow else {
            return
        }
                
        self.tableView.scrollToRow(at: IndexPath(row: 0, section: section), at: .none, animated: true)
        // bottomOffsetY <= 0 说明内容不超过一屏
        let bottomOffsetY = self.tableView.contentSize.height - self.tableView.bounds.size.height
        guard bottomOffsetY > 0 else {
            return
        }
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
            ///获取显示的cell
            guard let cell = self.tableView.cellForRow(at: IndexPath(row: 0, section: section)) as? ServicesListTableViewCell, 
            let subTableView = cell.subviews.first(where: {$0.tag == 888}) as? UITableView, 
            let subCell = subTableView.cellForRow(at: IndexPath(row: row, section: 0)) as? ServicesSubListTableViewCell else {
                return
            }
            
            let livingCellPoint = self.tableView.convert(CGPoint(x: subCell.frame.origin.x, y: subCell.frame.origin.y), from: subCell.superview)
            let offsetY = livingCellPoint.y > bottomOffsetY ? bottomOffsetY : livingCellPoint.y - 8
            self.tableView.setContentOffset(CGPoint.init(x: 0, y: offsetY), animated: true)
        }
   }

总结

当然使用设计模式去优化if代码,还有其他的设计模式,比如优先状态机等。

所有的方式也是可以组合使用。我们的目的是让代码更好阅读、更好扩展、更好维护。

不一定所有的if都要进行优化,一些简单的,本身来说就是很明确、很容易阅读、维护。那么其实就不需要优化。我们针对的是if过多、嵌套过多、不利于维护的代码。

最后,引用常说的一句话,要不因为使用设计模式而设计代码,这样反而得不偿失。