前言
设计模式利用碎片时间也看了不少,但是理解的不深入,很多时候,前面看着还很带劲,看着看着就没了兴致. 还是写写比较好.
主要参考 GOF的《设计模式》、《Head First 设计模式》、《Pro Design patters in Swift》、《iOS 设计模式解析》.还有网上各种. 我不会按顺序写,想到那个就写那个模式.因为有些模式看了好多遍而另些基本没看过!
今天访问者模式. 因为,今天正好看到了这个模式,并且脑海里没有这个模式的概念所以就先搞定它.
- 访问者模式
定义:
表示一个作用于某对象结构中的各个元素的操作。它让我们可以在不改变各个元素的类的前提下,定义作用于这些元素的新的操作。
分析:
- 它是一个操作-----行为型模式
- 操作的是某个对象中的各个元素
- 不改变原来的类, 添加新的操作 ---- 开闭原则
一个案例。定义几个形状,计算面积
import Foundation
class Circle {
let radius: Float;
init(radius: Float) {
self.radius = radius;
}
}
class Square {
let length: Float;
init(length: Float) {
self.length = length;
}
}
class Rectangle {
let xLen: Float;
let yLen: Float;
init(x: Float, y: Float) {
self.xLen = x;
self.yLen = y;
}
}
class ShapeCollecion {
let shapes:[Any];
init() {
shapes = [
Circle(radius: 2.5), Square(length: 4), Rectangle(x: 10, y: 2)
];
}
func calculateAreas() -> Float {
return shapes.reduce(0){ (total, shape)-> Float in
if let circle = shape as? Circle {
print("found circle");
return total + (3.14 * powf(circle.radius, 2));
} else if let square = shape as? Square {
print("found square");
return total + powf(square.length, 2);
} else if let rect = shape as? Rectangle {
print("found rectangle");
return total + rect.xLen * rect.yLen;
}
else {
return total;
}
}
}
}
let shapes = ShapeCollecion();
let area = shapes.calculateAreas();
print("area: \(area)");
使用Visitor重构
试想有如下新的需求,该如何实现。
- 去掉calculateAreas 中的if-else 因为可能会有其他形状
- 添加新的需求(方法,操作)如:计算所以形状的边数
- 当添加新的需求时,还不修改以前的类。(将计算法方法放在各种Shape中不合适)
import Foundation
protocol Shape {
func accept(visitor: Visitor)
}
protocol Visitor {
func visit(shape:Circle);
func visit(shape:Square);
func visit(shape:Rectangle);
}
class AreaVisitor: Visitor {
var totalArea: Float = 0;
func visit(shape: Circle) {
totalArea += (3.14 * powf(shape.radius, 2))
}
func visit(shape: Square) {
totalArea += powf(shape.length, 2)
}
func visit(shape: Rectangle) {
totalArea += shape.yLen * shape.xLen
}
}
class EdgesVisitor: Visitor {
var totalEdges = 0;
func visit(shape: Rectangle) {
totalEdges += 4;
}
func visit(shape: Square) {
totalEdges += 4;
}
func visit(shape: Circle) {
totalEdges += 1;
}
}
class Circle: Shape {
let radius: Float;
init(radius: Float) {
self.radius = radius;
}
func accept(visitor: Visitor) {
visitor.visit(shape: self);
}
}
class Square: Shape {
let length: Float;
init(length: Float) {
self.length = length;
}
func accept(visitor: Visitor) {
visitor.visit(shape: self);
}
}
class Rectangle: Shape {
let xLen: Float;
let yLen: Float;
init(x: Float, y: Float) {
self.xLen = x;
self.yLen = y;
}
func accept(visitor: Visitor) {
visitor.visit(shape: self);
}
}
class ShapeCollecion {
let shapes:[Shape];
init() {
shapes = [
Circle(radius: 2.5), Square(length: 4), Rectangle(x: 10, y: 2)
];
}
func accept(visitor: Visitor){
for shape in shapes {
shape.accept(visitor: visitor);
}
}
}
let shapes = ShapeCollecion();
let areaVisitor = AreaVisitor();
shapes.accept(visitor: areaVisitor);
print("Area: \(areaVisitor.totalArea)")
let edgeVistor = EdgesVisitor();
shapes.accept(visitor: edgeVistor);
print("Edges: \(edgeVistor.totalEdges)");
- 方法重载区分了形状,去掉了if-else
- 将各种操作封装成类。这些类遵循统一的协议。
- vistor 模式只能确保添加新的计算方法(边的个数)不修改各个客户类(Shape)
但如果,增加了新的Shape(客户类),就必须修改其他vistor,子类了。就不是vistor模式可以解决的了
知识点补充
二次分派技术的原理
①分派:根据对象的类型而对方法进行选择,就是分派。静态分派发生在编译时期,分派根据静态类型信息来指定方法,它是一个方法的静态绑定(如方法重载)。动态分派发生在运行时期,是根据接方法所属对象的实际类型来调用方法。动态类型绑定只会体现在方法的调用者身上,而方法的参数类型则会在编译期由编译器决定。
②单分派:只根据方法所属的对象的实际类型和参数的静态类型来指定调用的方法。
③多分派:是根据方法所属的对象的实际类型和参数的实际类型来指定调用的方法。
class Handler {
func handle(_ arg: FirstClass) {
print("First Class");
}
func handle(_ arg: SecondClass) {
print("second class")
}
func handle(_ arg: MyProtocol) {
print("Protocol")
}
}
protocol MyProtocol {
func dispatch(handler: Handler);
}
class FirstClass: MyProtocol {
func dispatch(handler: Handler) {
handler.handle(self);
}
}
class SecondClass: MyProtocol {
func dispatch(handler: Handler) {
handler.handle(self);
}
}
let objs:[MyProtocol] = [FirstClass(),SecondClass()];
let handler = Handler();
for obj in objs {
// handler.handle(obj);
obj.dispatch(handler: handler);
}