面向对象开发存在的问题
假如我们现在有两个类:Plane
和Bird
,这两个类中各有一个方法Fly()
考虑到封装和继承,有些同学可能为这两个类抽取一个父类,然后将Fly()
方法放于父类中.
的确,面向对象是一种不错的抽象方式,但是肯定不是最好的方式。因为飞机和鸟根本不是同一类事物,它无法描述两个不同事物(飞机和鸟)具有某个相同特性(飞)这一点的要求.其实利用一些特性的组合要比继承更让人接受一些。
Swfit协议的使用
其实面向协议的思想已经提出很多年了,很多经典书籍中都提出过面向接口编程,而不是面向实现编程
的概念。
那么究竟什么是面向协议编程呢?
简单来说,面向协议编程是在面向对象编程的基础上演变而来的,将程序设计过程中遇到的数据类型的抽象由使用基类进行抽取改为使用协议进行抽取。
协议的定义
//如果只希望协议被类遵守可以在协议后面加上 :class
protocol Flyable : class{
/*
*协议中既可以定义属性也可以定义方法
*注意:
1.协议中的属性和方法都不能有默认实现
2.在定义属性时,必须明确的指出该属性是一个可读可写/只读/只写
3.默认情况下protocol中的属性,必须被遵守协议的类/结构体实现
*/
var age : Int {get}
func fly()
}
默认情况下,遵守协议的类或者结构体必须实现协议中的方法或者是属性,那么如果我们希望协议中的方法或者属性是可选的,该如何操作呢?
/*
*条件:
1.必须在protocol的前面加上@objc
2.在方法或者是属性的前面加上@objc + optional
*/
@objc protocol Flyable : class{
@objc optional var age : Int {get}
@objc optional func fly()
}
遵守某个协议的类的对象调用协议声明的方法时,如果类本身没有提供实现,协议扩展提供的默认实现会被调用。
protocol Flyable : class{}
//默认实现条件:必须在协议的extension中实现
extension Flyable
{
var age : Int
{
return 3
}
func fly()
{
print("在天空中翱翔...")
}
}
面向协议应用
控件抖动效果
比如上个页面,当用户点击登录的时候如果用户输入的用户名或者是密码错误的时候我们想让用户名和密
码控件做一个颤抖的小动画,当然有可能其他页面也会有这个效果,比如注册页面,用户调查页面,我想说的
是这个小动画有可能会被重复利用多次,那么我们该怎么设计这种方案会更好呢?
首先大家可能会想给 UIView
增加一个 Category
, 的确这样可以解决这个问题.但是不知道大家有没有思考过,如果我们给 UIView
加上这么一个 Category
,那么所有的View也将会有这个功能,即使他们根本不需要这个功能.
当然也有人可能会说那么如果我们抽象出来一个继承自 UIView
的基类 BaseView
,在该类中实现动画效果然后所有需要这个动画的控件都继承它呢? 这种方法当然可以解决这个问题,但是同时也会产生一些其他的问题 比如,现在需要给上图中的 用户名
控件 再增加一个功能,而这个功能又依赖于另外几个类。因为Swift/Objective-C只能单继承,所以这样做是不可以的. 而如果我们把新的功能实现也放到这个 BaseView
里,就引入了不必要的耦合。
但是如果我们利用 Swift的面向协议编程,我们就可以轻松的解决这个问题:
import UIKit
protocol Shakeable{
}
extension Shakeable where Self : UIView
{
func ShakeView()
{
translatesAutoresizingMaskIntoConstraints = true
let posLbl = layer.position
let animation = CABasicAnimation(keyPath: "position")
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
animation.fromValue = NSValue(cgPoint:CGPoint(x: posLbl.x - 10, y: posLbl.y) )
animation.fromValue = NSValue(cgPoint:CGPoint(x: posLbl.x + 10, y: posLbl.y) )
animation.autoreverses = true
animation.duration = 0.04
animation.repeatCount = 6
layer.add(animation, forKey: nil)
translatesAutoresizingMaskIntoConstraints = false
}
}
定义好该协议并且默认实现了颤抖的方法以后,如果控件想实现颤抖的功能,比如当登录按钮, 此时我们只需要让该按钮遵守这个协议即可,其他什么都不需要操作了,这样就可以调用 ShakeView()
这个方法了
class loginButton :UIButton , AnimateAble{}
这样使用起来是不是更加灵活了呢
加载Xib文件
在开发中我们经常会用Xib文件去描述一个页面,所以加载Xib文件的这坨代码就会经常重复Copy,那么我们可以利用面向协议的思想抽取出来,使用的地方只需要遵守该协议即可,使用起来更加灵活.
import UIKit
protocol NibLoadable {
}
extension NibLoadable where Self : UIView {
static func loadFromNib(_ nibname : String? = nil) -> Self {
let loadName = nibname == nil ? "\(self)" : nibname!
return Bundle.main.loadNibNamed(loadName, owner: nil, options: nil)?.first as! Self
}
}
其实开发中有非常多的地方可以使用面向协议的思想抽取出来,这样会更加灵活.我们可以像搭积木或者说是组装车一样,而且其它新项目需要,我们完全可以直接复制过去,而不需要担心因为多继承的关系而复制多份文件
但是并不是说面向协议就比面向对象更好,面向协议编程是在面向对象编程的基础上演变而来的,面向对象肯定是基础,只不过有些地方使用面向对象可能会造成一些问题,这个时候我们就可以使用面向协议的思想来对面向对象做一个补充了.