iOS&设计模式 - 构建者模式(Builder)

什么是构建都模式

将一个复杂对象的创建和表示进行分离,根据创建顺序不一样,表示的方式也不一样。如买手机时选择配置:iPhote7、玫瑰金、64g,你可以选择不同配置,但你却不知道他是如果实现的,同时也因为的选择配置的不同,生产的工艺和手机样式也不一样。用户不需要知道如何实现的,只选择最终的表示,这就是现实生活中构建者模式。

标准组成

  • 抽象构建器(Builder):为构建器提供接口(iOS 中的协议)
  • 具体构建器(ConcreteBuilder):构建具体的产品(实现 Builder 协议)
  • 指挥者(Director):调用Builder接口來创建产品对象(这个不一定需要,可有可无)
  • 产品角色(Product):具体的产品

结构

图片.png

图上的指挥者(组装器)是不一定需要的,因为我们可以直接 iPhoneBuilder 来组装产品,而不需要再单独封闭一个类来实现,但要与不要,还是需要根据业务场景来确定,设计模式是死的,人是活的,至于怎么使用,还是那句话:按实际需求。

实现

这里实现一个日期的构建者:

// let bulider = DateBulider()
// bulider.setYear(2017).setMonth(5).setDay(25).setHour(11).setMinute(41).showDate()

class DateBulider: NSObject {  
    private var component: NSDateComponents?
    override init() {
        component = NSDateComponents()
        // default
        component?.calendar = Calendar(identifier: .gregorian)
    }
    func date() -> Date {
        return (component?.date)!
    }
    func showDate() {
        let formatter = DateFormatter()
        formatter.dateFormat = "yy-MM-dd hh:mm:ss"
        print(formatter.string(from: (component?.date)!))
    }
    func setCalender(identifier: Calendar.Identifier)  -> DateBulider {
        component?.calendar = Calendar(identifier: identifier)
        return self
    }
    func setSecond(_ second: Int) -> DateBulider {
        component?.second = second
        return self
    }
    func setMinute(_ minute: Int) -> DateBulider {
        component?.minute = minute
        return self
    }
    func setHour(_ hour: Int) -> DateBulider {
        component?.hour = hour
        return self
    }
    func setDay(_ day: Int) -> DateBulider {
        component?.day = day
        return self
    }
    func setMonth(_ month: Int) -> DateBulider {
        component?.month = month
        return self
    }
    func setYear(_ year: Int) -> DateBulider {
        component?.year = year
        return self
    }
}

上例的构建者,非严格标准的构建者,下面按标准来做,实现抽象构建器(DateBuilder Protocole):

protocol DateBuliderProtocole: class {
    func date() -> Date
    func showDate()
    func setCalender(identifier: Calendar.Identifier)  -> DateBulider
    func setSecond(_ second: Int) -> DateBulider
    func setMinute(_ minute: Int) -> DateBulider
    func setHour(_ hour: Int) -> DateBulider
    func setDay(_ day: Int) -> DateBulider
    func setMonth(_ month: Int) -> DateBulider
    func setYear(_ year: Int) -> DateBulider
}

并在 DateBulider 实现该 Protocole:

class DateBulider: DateBuliderProtocole {...}

到这里,这个例子可以说是比较标准的简单的构建者模式,Director 和 Product 并没有出现,但 Director 是不一定需要的,Product 部分也可以说是:

let bulider = DateBulider()
bulider.setYear(2017).setMonth(5).setDay(25).setHour(11).setMinute(41).showDate()

但这里没有封装成一个 Product 类,如果在复杂的场景下,Product 类还是需要有的。

非标准构建者模式

设计模式是一种思想,不是一成不变的,就构建者模式来说,实际中使用构建者模式很多者是标准模式的中变形,不过思想都是一样的,万变不离其宗。

下面了解下 Realm 中使用到的构建者模式,关于 Realm 的介绍就不多说了,一句话:

第一个专门针对移动平台设计的数据库,目标是取代SQLite。

Realm 中的数据库操作就是使用构建者模式,如:

// 添加/更新
realm.add(model, update: true)

// 删除
realm.delete(model)

// 查询条件
realm.objects(YHFeedItemModel.self).filter("isDel = false").filter("isFavorite = true")

这里就是构建者模式的一种应用,将一个复杂对象的创建和表示进行分离。如添加功能中,我们输入正确的实例就可以完成保存,但底层他到底是怎么实现的我们并不知道,而且我们也不关心。我们可以下载 Realm 的源码来看下的实现(大概知道流程就可以了,有兴趣的可以单独去下完整的源码看):

// Swift 
public func add(_ object: Object, update: Bool = false) {
    if update && object.objectSchema.primaryKeyProperty == nil {
        throwRealmException("'\(object.objectSchema.className)' does not have a primary key and can not be updated")
    }
    RLMAddObjectToRealm(object, rlmRealm, update)
}

// OC
void RLMAddObjectToRealm(__unsafe_unretained RLMObjectBase *const object,
                         __unsafe_unretained RLMRealm *const realm,
                         bool createOrUpdate) {
    RLMVerifyInWriteTransaction(realm);

    // verify that object is unmanaged
    if (object.invalidated) {
        @throw RLMException(@"Adding a deleted or invalidated object to a Realm is not permitted");
    }
    if (object->_realm) {
        if (object->_realm == realm) {
            // Adding an object to the Realm it's already manged by is a no-op
            return;
        }
        // for differing realms users must explicitly create the object in the second realm
        @throw RLMException(@"Object is already managed by another Realm. Use create instead to copy it into this Realm.");
    }
    if (object->_observationInfo && object->_observationInfo->hasObservers()) {
        @throw RLMException(@"Cannot add an object with observers to a Realm");
    }

    auto& info = realm->_info[object->_objectSchema.className];
    RLMAccessorContext c{realm, info, true};
    object->_info = &info;
    object->_realm = realm;
    object->_objectSchema = info.rlmObjectSchema;
    try {
        realm::Object::create(c, realm->_realm, *info.objectSchema, (id)object,
                              createOrUpdate, &object->_row);
    }
    catch (std::exception const& e) {
        @throw RLMException(e);
    }
    object_setClass(object, info.rlmObjectSchema.accessorClass);
    RLMInitializeSwiftAccessorGenerics(object);
}

这里就不详细读 Realm 的源码了,大概了解它的实现就可以了,主要是理解构建者模式的思想,关于构建者模式的内容就说到这里了,有空闲时候话,再简单写一个复杂一点 Demo 来补充说明了。

Demo可以从这里下载

-------------一般般的分割线 17.07.23 更新-------------

通过构建者实现自定义 UIAlertView

在最近开发中,需要实现一个支持添加图片的 AlertView,顺便也实践下构建者模块,直接上代码:

class JCAlertView: NSObject {  
    private var alertView: UIAlertView!
    private override init() {}
    static func bulid() -> JCAlertView {
        let alertView = UIAlertView()
        let alert = JCAlertView()
        alert.alertView = alertView
        return alert
    }
    public func setDelegate(_ delegate: AnyObject?) -> JCAlertView {
        alertView.delegate = delegate
        return self
    }
    public func setTitle(_ title: String) -> JCAlertView {
        alertView.title = title
        return self
    }
    public func setMessage(_ message: String) -> JCAlertView {
        alertView.message = message
        return self
    }
    public func setTag(_ tag: Int) -> JCAlertView {
        alertView.tag = tag
        return self
    }
    public func addButton(_ buttonTitle: String) -> JCAlertView {
        alertView.addButton(withTitle: buttonTitle)
        return self
    }
    public func addCancelButton(_ buttonTitle: String) -> JCAlertView {
        alertView.addButton(withTitle: buttonTitle)
        let count = alertView.numberOfButtons
        alertView.cancelButtonIndex = count - 1
        return self
    }
    public func  addImage(_ image: UIImage) -> JCAlertView {
        let imageView = UIImageView()
        let scale = 270 / image.size.width
        imageView.image = image.resizeImage(image: image, newSize: CGSize(width: image.size.width * scale, height: image.size.height * scale))
        alertView.setValue(imageView, forKey: "accessoryView")
        return self
    }
    public func show() {
        alertView.show()
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容