项目Demo

本文代码言语为Objective-C

接着上篇文章对规划形式七大准则的理解,这篇文章则是结合代码的比如,学习第一种形式:创立型形式

在面向目标编程中,创立型形式是用于实例化目标的规划形式。它们能够帮助咱们有效地创立目标,而不会使代码变得杂乱或难以保护。

在实践的开发进程中,正确运用创立型形式能够带来许多优点:

  1. 下降耦合性:创立型形式能够将目标的创立进程与运用进程别离,然后下降目标之间的耦合性。这使得代码愈加灵敏和易于保护。
  2. 进步复用性:创立型形式能够将目标的创立进程笼统出来,并使其可复用。这使得咱们能够在整个应用程序中同享目标的创立逻辑,然后进步了代码的复用性。
  3. 躲藏目标的创立细节:运用创立型形式能够将目标的创立细节躲藏在形式内部,使得客户端无需知道目标创立的详细细节。这使得代码愈加简练、清晰,并进步了代码的可读性。
  4. 管理目标的生命周期:某些创立型形式(如工厂办法形式)能够管理目标的生命周期,然后保证目标的正确创立、运用和毁掉。
  5. 改进代码的可测试性:运用创立型形式能够使代码愈加易于测试。由于目标的创立进程被笼统出来而且与运用进程别离,因而能够更容易地编写测试用例

0 常见的创立型形式

  1. 简略工厂形式(Simple Factory Pattern)
  2. 工厂办法形式(Factory Method Pattern)
  3. 笼统工厂形式(Abstract Factory Pattern)
  4. 单例形式(Singleton Pattern)
  5. 原形形式(Prototype Pattern)
  6. 制作者形式(Builder Pattern)

1 简略工厂形式(Simple Factory Pattern)

24种设计模式代码实例学习(二)创建型模式

在工厂形式中,咱们在创立目标时,经过运用一个共同的接口来创立不同的目标,而无需暴露目标的创立逻辑

在简略工厂形式中,咱们界说一个工厂类,该工厂类担任创立各种目标。咱们只需调用工厂类的办法即可获取需求的目标,而无需直接运用 new 关键字创立目标。这样能够使代码愈加灵敏和易于保护,由于假如咱们想要更改创立目标的逻辑,只需求更改工厂类中的代码即可。

比如

简略运用:Animal

假设咱们有一个 Animal 类和两个子类 DogCat,咱们想要运用工厂形式来创立它们的实例:

// Animal.h
@interface Animal : NSObject
@property (nonatomic, copy) NSString *name;
- (void)speak;
@end
// Dog.h
@interface Dog : Animal
@end
// Cat.h
@interface Cat : Animal
@end
// Animal.m
@implementation Animal
- (void)speak {
    NSLog(@"I am an animal");
}
@end
// Dog.m
@implementation Dog
- (void)speak {
    NSLog(@"I am a dog");
}
@end
// Cat.m
@implementation Cat
- (void)speak {
    NSLog(@"I am a cat");
}
@end
// AnimalFactory.h
@interface AnimalFactory : NSObject
+ (Animal *)createAnimalWithType:(NSString *)type;
@end
// AnimalFactory.m
@implementation AnimalFactory
+ (Animal *)createAnimalWithType:(NSString *)type {
    if ([type isEqualToString:@"dog"]) {
        return [[Dog alloc] init];
    } else if ([type isEqualToString:@"cat"]) {
        return [[Cat alloc] init];
    } else {
        return nil;
    }
}
@end

在上面的代码中,咱们界说了一个 Animal 类和两个子类 DogCat。咱们还界说了一个 AnimalFactory 工厂类,其间有一个 createAnimalWithType: 办法,该办法依据传入的 type 参数来创立不同类型的动物目标。当咱们需求创立动物目标时,只需调用 AnimalFactorycreateAnimalWithType: 办法,传入所需的类型,即可获取相应的目标。

例如,咱们能够经过以下代码创立一只狗:

Animal *dog = [AnimalFactory createAnimalWithType:@"dog"];
[dog speak];  // 输出 "I am a dog"

相同,咱们能够经过以下代码创立一只猫:

Animal *cat = [AnimalFactory createAnimalWithType:@"cat"];
[cat speak];  // 输出 "I am a cat"

经过这种办法,咱们能够在不暴露目标创立逻辑的状况下创立不同类型的目标,而且能够轻松地增加新类型的动物,只需在 AnimalFactory 中增加相应的创立逻辑即可。

在实践开发中,简略工厂形式常常用于以下场景:

  1. UIKit 中的控件创立:UIKit 中的各种控件(如 UIButtonUILabel 等)都是经过工厂形式创立的。
  2. 网络恳求中的恳求工厂:在进行网络恳求时,能够经过恳求工厂创立各种类型的恳求目标,如 GET 恳求、POST恳求等。

UIKit 中的控件创立

在iOS开发中,UIKit结构供给了许多用于构建用户界面的控件,如 UIButton、UILabel、UITextField 等。这些控件的创立是经过工厂形式完成的。

在UIKit中,控件的创立都是由一个叫做 UIFactory 的工厂类担任的。UIFactory类供给了多个类办法,用于创立不同类型的控件。例如,UIFactory类中的 buttonWithType: 办法用于创立 UIButton 目标,示例代码如下:

UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];

在上面的代码中,buttonWithType: 是一个工厂办法,它承受一个 UIButtonType 枚举类型的参数,并回来一个 UIButton 目标。在实践运用中,咱们能够经过传递不同的参数,创立不同类型的按钮。

除了 UIButton,其他的控件也是经过类似的办法创立的。例如,UILabel控件的创立代码如下:

UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];

在上面的代码中,initWithFrame: 是一个结构办法,它承受一个 CGRect 类型的参数,并回来一个 UILabel 目标。不同于 UIButtonUILabel并没有供给工厂办法来创立目标,而是经过结构办法创立目标。

上面的UILabel没有运用 UIFactory 类供给的工厂办法来创立 UILabel 目标,可是它的创立依然归于工厂形式。这个创立进程背后依然有一个专门的工厂类(例如 UILabel初始化办法会调用父类 UIView 的初始化办法),而且这个工厂类也有着与工厂形式类似的特征

  1. 工厂类担任创立详细的目标:在 UIKit 中,UILabel 目标的创立进程由 UIView 类的初始化办法担任,UIViewUILabel 的父类,能够看做是 UILabel 目标的工厂类。
  2. 工厂类躲藏了目标的创立细节:咱们并不需求知道 UILabel 目标是怎么被创立的,只需求知道它的初始化办法即可。
  3. 工厂类能够供给多个不同的创立办法:在 UIKit 中,除了 [[UILabel alloc] initWithFrame:CGRectZero] 这种创立办法之外,还有许多其他的创立办法,例如 [[UILabel alloc] init][[UILabel alloc] initWithCoder:] 等等,这些办法都能够用于创立 UILabel 目标。

因而,即使咱们没有运用 UIFactory 类供给的工厂办法来创立 UILabel 目标,UILabel 目标的创立进程依然归于工厂形式的领域。

网络恳求中的恳求工厂

在进行网络恳求时,能够经过恳求工厂创立各种类型的恳求目标,如 GET 恳求、POST 恳求等。一般状况下,网络恳求的参数和恳求头号信息都需求在发送恳求前进行装备,为了防止在客户端代码中重复编写这些代码,能够运用简略工厂形式将恳求目标的创立和装备进程封装起来,使得客户端代码愈加简练易于保护

恳求工厂一般包含一个或多个工厂办法,用于创立不同类型的恳求目标。在创立恳求目标时,能够经过传递不同的参数,来区分不同类型的恳求

以下是一个简略的网络恳求工厂示例,也借助了开发中比较流行的网络库AFNetworking来完成:

首要先写出网络恳求类:

@interface NetworkManager : NSObject
/// 创立单例
+ (instancetype _Nonnull)shareManager;
/// 恳求类型
typedef NS_ENUM(NSUInteger, NetworkManagerRequestType) {
    NetworkManagerRequestTypeGet,
    NetworkManagerRequestTypePost,
    NetworkManagerRequestTypePut,
    NetworkManagerRequestTypeDelete
};
/// 一致恳求办法 responseSerializer默认都为AFJSONResponseSerializer
- (void)requestURL:(NSString * _Nonnull)url
              type:(NetworkManagerRequestType)requestType
        parameters:(id _Nonnull)parameters
          progress:(void (^_Nullable)(NSProgress * _Nullable progress))progress
           success:(void (^)(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject))success
           failure:(void (^)(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error))failure;
@end
#import "NetworkManager.h"
#import <AFNetworking/AFNetworking.h>
@interface NetworkManager ()
/// AFHTTPSessionManager
@property (nonatomic, strong) AFHTTPSessionManager *sessionManager;
@end
@implementation NetworkManager
/// 创立单例
+ (instancetype _Nonnull)shareManager {
    static dispatch_once_t onceToken;
    static NetworkManager *_shareManager = nil;
    dispatch_once(&onceToken, ^{
        _shareManager = [[self alloc] init];
    });
    return _shareManager;
}
- (instancetype)init {
    self = [super init];
    if (self) {
        self.sessionManager = [AFHTTPSessionManager manager];
        self.sessionManager.requestSerializer = [AFJSONRequestSerializer serializer];
        // 默认上传JSON 格式
        self.sessionManager.responseSerializer = [AFJSONResponseSerializer serializer];
    }
    return self;
}
/// 一致恳求办法 responseSerializer默认都为AFJSONResponseSerializer
- (void)requestURL:(NSString * _Nonnull)url
              type:(NetworkManagerRequestType)requestType
        parameters:(id _Nonnull)parameters
          progress:(void (^_Nullable)(NSProgress * _Nullable progress))progress
           success:(void (^)(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject))success
           failure:(void (^)(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error))failure {
    switch (requestType) {
        case NetworkManagerRequestTypeGet:
            [self GETRequestURL:url
                     parameters:parameters
                       progress:progress
                        success:success
                        failure:failure];
            break;
        case NetworkManagerRequestTypePost:
            [self POSTRequestURL:url
                     parameters:parameters
                       progress:progress
                        success:success
                        failure:failure];
            break;
        case NetworkManagerRequestTypePut:
            [self PUTRequestURL:url
                     parameters:parameters
                       progress:progress
                        success:success
                        failure:failure];
            break;
        case NetworkManagerRequestTypeDelete:
            [self DELETERequestURL:url
                     parameters:parameters
                       progress:progress
                        success:success
                        failure:failure];
            break;
        default:
            break;
    }
}
/// GET 恳求
- (void)GETRequestURL:(NSString * _Nonnull)url
           parameters:(id _Nonnull)parameters
             progress:(void (^_Nullable)(NSProgress * _Nullable progress))progress
              success:(void (^)(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject))success
              failure:(void (^)(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error))failure {
    [self.sessionManager
     GET:url
     parameters:parameters
     progress:progress
     success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        if (success) {
            success(task, responseObject);
        }
    }
     failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        if (failure) {
            failure(task, error);
        }
    }];
}
/// POST
- (void)POSTRequestURL:(NSString * _Nonnull)url
           parameters:(id _Nonnull)parameters
             progress:(void (^_Nullable)(NSProgress * _Nullable progress))progress
              success:(void (^)(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject))success
              failure:(void (^)(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error))failure {
    [self.sessionManager
     POST:url
     parameters:parameters
     progress:progress
     success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        if (success) {
            success(task, responseObject);
        }
    }
     failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        if (failure) {
            failure(task, error);
        }
    }];
}
/// PUT
- (void)PUTRequestURL:(NSString * _Nonnull)url
           parameters:(id _Nonnull)parameters
             progress:(void (^_Nullable)(NSProgress * _Nullable progress))progress
              success:(void (^)(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject))success
              failure:(void (^)(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error))failure {
    [self.sessionManager PUT:url parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        success(task, responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        failure(task, error);
    }];
}
/// DELETE
- (void)DELETERequestURL:(NSString * _Nonnull)url
              parameters:(id _Nonnull)parameters
                progress:(void (^_Nullable)(NSProgress * _Nullable progress))progress
                 success:(void (^)(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject))success
                 failure:(void (^)(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error))failure {
    [self.sessionManager DELETE:url parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        success(task, responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        failure(task, error);
    }];
}
@end

工厂类:

#import "NetworkManager.h"
NS_ASSUME_NONNULL_BEGIN
@interface NetworkRequestFactory : NSObject
+ (NetworkManager *)creatNetworkManager;
@end
@implementation NetworkRequestFactory
+ (NetworkManager *)creatNetworkManager {
    return [NetworkManager shareManager];
}
@end

运用时能够这样调用:

NetworkManager *networkManager = [NetworkRequestFactory
creatNetworkManager];
[networkManager
 requestURL:@"http://example.com/api"
 type:NetworkManagerRequestTypeGet
 parameters:nil
 progress:nil
 success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable
responseObject) {
}
 failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull
error) {
}];

这样能够经过简略的代码调用,方便地创立不同类型的网络恳求,而且能够方便地切换底层网络库的完成。

2 工厂办法形式(Factory Method Pattern)

工厂办法形式和简略工厂形式类似。

简略工厂形式和工厂办法形式的区别在于创立目标的办法

简略工厂形式运用一个同享的工厂类来创立一切类型的目标,客户端经过传递参数来指定要创立的目标类型。

而工厂办法形式将每种详细产品的创立逻辑委托给对应的工厂类,客户端经过选择特定的工厂类来创立特定类型的目标

简略工厂形式适用于目标类型较少且创立逻辑相对简略的状况。工厂办法形式适用于目标类型较多或创立逻辑较为杂乱的状况,它更符合开闭准则,答应新增详细产品和工厂类,而无需修正现有代码。

比如

// 产品接口
@protocol ShapeProtocol <NSObject>
- (void)draw;
@end
// 详细产品A
@interface Circle : NSObject <ShapeProtocol>
@end
@implementation Circle
- (void)draw {
    NSLog(@"制作圆形");
}
@end
// 详细产品B
@interface Rectangle : NSObject <ShapeProtocol>
@end
@implementation Rectangle
- (void)draw {
    NSLog(@"制作矩形");
}
@end
// 笼统工厂
@protocol ShapeFactoryProtocol <NSObject>
- (id<ShapeProtocol>)createShape;
@end
// 详细工厂A
@interface CircleFactory : NSObject <ShapeFactoryProtocol>
@end
@implementation CircleFactory
- (id<ShapeProtocol>)createShape {
    return [[Circle alloc] init];
}
@end
// 详细工厂B
@interface RectangleFactory : NSObject <ShapeFactoryProtocol> 
@end
@implementation RectangleFactory
- (id<ShapeProtocol>)createShape { 
    return [[Rectangle alloc] init]; 
} 
@end

客户端运用:

id<ShapeFactoryProtocol> factory = [[CircleFactory alloc] init]; 
id<ShapeProtocol> shape = [factory createShape]; 
[shape draw];

3 笼统工厂形式(Abstract Factory Pattern)

24种设计模式代码实例学习(二)创建型模式

它供给一个接口用于创立一系列相关或依靠目标的宗族,而不需求明确指定它们的详细类。

在笼统工厂形式中,笼统工厂接口界说了一组办法,用于创立一系列相关目标的工厂。这些相关目标能够是一组不同但相关的目标,例如在GUI界面中,按钮、标签、输入框等都归于相关的目标宗族,它们一般会按照某种风格规划,如iOS 风格、Android 风格等。在笼统工厂形式中,每个详细工厂类都完成了这个笼统工厂接口,以便能够按照某种特定的规则或风格创立相关目标的宗族。

能够说笼统工厂形式是工厂形式的一个扩展,它供给了一种更高层次的笼统,能够创立一组相关的目标宗族,而不仅仅是一个目标

一起,笼统工厂形式也遵循了开闭准则,由于它答应在不修正现有代码的状况下增加新的详细工厂和产品系列,然后进步了代码的可保护性和可扩展性。

比如

咱们要写两个风格的UI,分别是iOS 风格和Android 风格,就像上面图片中的球形风格和金字塔风格,都是相同的产品,可是是不同风格,运用笼统工厂形式的代码如下,

首要,咱们界说一个笼统工厂接口,它包含两个办法:创立按钮和创立标签。

@protocol AbstractFactoryProtocol <NSObject>
- (UIButton *)createButton;
- (UILabel *)createLabel;
@end

然后,咱们创立两个详细工厂类,它们完成了这个笼统工厂接口,并分别创立了iOS 和Android 风格的按钮和标签。

// iOS风格的详细工厂类
@interface iOSFactory : NSObject <AbstractFactoryProtocol>
@end
@implementation iOSFactory
- (UIButton *)createButton {
    return [UIButton buttonWithType:UIButtonTypeSystem];
}
- (UILabel *)createLabel {
    return [[UILabel alloc] init];
}
@end
// Android风格的详细工厂类
@interface AndroidFactory : NSObject <AbstractFactoryProtocol>
@end
@implementation AndroidFactory
- (UIButton *)createButton {
    return [UIButton buttonWithType:UIButtonTypeRoundedRect];
}
- (UILabel *)createLabel {
    return [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
}
@end

最后,咱们运用这些详细工厂类来创立详细的按钮和标签目标,而不需求知道它们的详细类。

id <AbstractFactoryProtocol> factory = [[iOSFactory alloc] init];
// 运用iOS风格的按钮和标签
[factory creatButton];
[factory creatLabel];
// 运用Android风格的按钮和标签
[factory creatButton];
[factory creatLabel];

经过运用笼统工厂形式,咱们能够轻松地扩展工厂和产品系列,而不需求修正现有的客户端代码。例如,咱们能够创立一个新的详细工厂类,以创立Windows风格的按钮和标签。

笼统工厂形式的优点:

  1. 将目标的创立与运用别离,客户端只需求知道怎么运用这些目标,而不需求关怀它们是怎么创立的。
  2. 能够轻松地在程序中切换不同的产品簇,只需求改变详细工厂类即可,无需修正客户端代码。
  3. 能够保证一组相关的产品只能由同一个工厂创立,防止了不同产品之间的兼容性问题。
  4. 能够进步代码的扩展性和可保护性,假如需求增加一个新的产品簇,只需求增加一个新的详细工厂类即可

笼统工厂形式的缺陷:

  1. 新增加一个产品簇比较困难,需求修正笼统工厂接口和一切的详细工厂类。
  2. 假如产品簇过多,会导致笼统工厂接口和一切的详细工厂类变得非常杂乱,不易保护。
  3. 由于笼统工厂形式需求界说笼统工厂接口和笼统产品接口,因而代码的笼统层次比较高,有必定的学习成本。

总归,笼统工厂形式适合于那些需求一整套一同运用的目标(比如QQ 换皮肤),而且需求能够方便地切换不同的产品簇的场景,可是假如产品簇过多或许需求频频增加新的产品簇,或许会导致代码变得杂乱。

产品簇

指的是一组相相关的产品,它们一般用于处理同一个问题或许满足同一种需求。例如,在电商网站上,一个产品簇能够包含产品、订单、收货地址等多个相相关的产品;在游戏开发中,一个产品簇能够包含人物、武器、装备等多相相关的产品。

4 单例形式(Singleton Pattern)

24种设计模式代码实例学习(二)创建型模式

单例形式是一种创立目标的规划形式,它保证一个类只要一个实例,并供给大局拜访点以拜访该实例。

单例形式一般用于需求大局拜访且只需求一个实例的场景。下面是一些常见的用例:

  1. 装备目标:在应用程序中,或许需求一个大局装备目标,用于存储应用程序的设置和装备选项。运用单例形式能够保证只要一个装备目标,并使其在整个应用程序中易于拜访。
  2. 数据库衔接目标:在运用数据库的应用程序中,或许需求一个大局的数据库衔接目标,用于履行查询和更新操作。运用单例形式能够保证只要一个数据库衔接目标,并使其在整个应用程序中易于拜访。
  3. 日志目标:在应用程序中,或许需求一个大局的日志目标,用于记载应用程序的运行时信息。运用单例形式能够保证只要一个日志目标,并使其在整个应用程序中易于拜访。

总归,单例形式在需求大局拜访且只需求一个实例的场景下非常有用。可是,运用单例形式也存在一些缺陷,例如或许导致代码耦合性增强、单例目标的生命周期或许过长等问题。因而,在运用单例形式时需求慎重考虑其适用性和运用办法。

比如

Objective-C中,单例形式能够经过运用静态变量和类办法来完成。以下是一个简略用于网络恳求中的比如:

@interface NetworkSingleton : NSObject
/// 创立单例
+ (instancetype _Nonnull)shareManager;
@end
@implementation NetworkSingleton
/// 创立单例
+ (instancetype _Nonnull)shareManager {
    static dispatch_once_t onceToken;
    static NetworkSingleton *_shareManager = nil;
    dispatch_once(&onceToken, ^{
        _shareManager = [[self alloc] init];
    });
    return _shareManager;
}
@end

5 原形形式(Prototype Pattern)

24种设计模式代码实例学习(二)创建型模式

原形形式答应经过仿制现有目标来创立新目标,而不是经过实例化新的目标。这种形式一般用于创立杂乱的目标,由于创立这些目标需求很多时间和资源。

原型形式一般在以下场景中运用:

  1. 创立杂乱目标:假如要创立的目标比较杂乱,例如具有多个子目标或需求很多核算才干创立,那么运用原型形式能够防止重复的核算和杂乱的目标构建进程。
  2. 进步功能:运用原型形式能够进步功能,由于仿制现有目标比创立新目标要
  3. 保护目标:有时候,为了保护目标的不变性,期望目标不能被修正。在这种状况下,运用原型形式能够保证不会修正原始目标,由于只能仿制它而不是直接修正它。
  4. 动态装备目标:假如要在运行时动态装备目标,例如依据用户输入或环境变量来装备目标的特点,那么运用原型形式能够方便地生成新目标。

它一般与工厂形式和制作者形式一同运用,以供给更好的灵敏性和可保护性。

在实践的开发中,或许会遇到需求创立杂乱目标的场景,例如:

  1. 图形化界面:开发一个图形化界面,或许需求创立许多杂乱的图形目标,例如窗口、按钮、标签、文本框等。
  2. 游戏开发:或许需求创立许多杂乱的游戏目标,例如人物、怪物、道具等。
  3. 数据库拜访层:需求创立许多杂乱的数据目标,例如表格、列、行等。
  4. 网络通信:开发一个网络应用程序,或许需求创立许多杂乱的网络目标,例如恳求、响应、协议等。

在这些场景中,杂乱目标一般包含多个子目标或特点,而且或许需求履行多个核算或操作才干构建。运用原型形式能够防止在每次需求创立目标时履行这些核算或操作,而且能够进步功能和可保护性。

Objective-C中,原型形式一般运用NSCopying协议来完成。NSCopying协议界说了一个copyWithZone办法,该办法回来目标的副本。

比如

图形化界面

在 iOS 开发中,创立一个杂乱的界面或许涉及许多目标,例如UIViewUILabelUIButton等。咱们能够运用原型形式来创立这些目标,而不需求每次都手动创立和装备它们。

@interface MyView : UIView <NSCopying>
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIButton *button;
@end
@implementation MyView
// 完成仿制办法
- (instancetype)copyWithZone:(NSZone *)zone {
    MyView *copyView = [[[self class] allocWithZone:zone] init];
    copyView.titleLabel = self.titleLabel;
    copyView.button = self.button;
    return copyView;
}
@end

运用时:

// 创立原型目标
MyView *prototypeView = [[MyView alloc] init];
prototypeView.titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
prototypeView.button = [[UIButton alloc] initWithFrame:CGRectMake(0, 60, 100, 50)];
// 仿制原型目标,创立新的目标
MyView *newView = [prototypeView copy];
newView.titleLabel.text = @"Hello word";
[newView.button setTitle:@"Click" forState:UIControlStateNormal];

在上面的比如中,咱们创立了一个包含多个子目标的 UIView子类 MyView,并界说了一个仿制办法来完成目标的仿制。然后,咱们创立了一个原型目标 prototypeView,并设置了它的子目标 titleLabelbutton。最后,咱们仿制了原型目标,并在新目标 newView 中修正了 titleLabelbutton 的特点。

原型形式与工厂形式的结合运用

工厂形式用于创立杂乱目标,而原型形式则能够用于快速仿制这些杂乱目标。 这种结合运用能够进步目标的创立功率和功能

例如,在一个电子商务网站中,咱们或许需求创立多种不同类型的产品,并依据用户的需求动态选择产品类型。在这种状况下,咱们能够运用工厂形式创立原型实例,然后运用原型实例的仿制办法创立新的目标实例

// Product类
@interface Product : NSObject <NSCopying>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) CGFloat price;
@end
@implementation Product
- (id)copyWithZone:(NSZone *)zone {
    Product *productCopy = [[[self class] allocWithZone:zone] init];
    productCopy.name = self.name;
    productCopy.price = self.price;
    return productCopy;
}
@end
// ProductFactory类
@interface ProductFactory : NSObject
@property (nonatomic, strong) Product *productPrototype;
- (Product *)createProduct;
@end
@implementation ProductFactory
- (Product *)createProduct {
    return [self.productPrototype copy];
}
@end
// 运用示例
Product *prototype = [[Product alloc] init];
prototype.name = @"iPhone";
prototype.price = 1000.0;
ProductFactory *factory = [[ProductFactory alloc] init];
factory.productPrototype = prototype;
Product *product1 = [factory createProduct]; // 创立新的产品实例1
product1.price = 1200.0;
Product *product2 = [factory createProduct]; // 创立新的产品实例2
product2.name = @"iPad";
NSLog(@"Prototype: %@", prototype);
NSLog(@"Product 1: %@", product1);
NSLog(@"Product 2: %@", product2);

在上述代码示例中,咱们界说了一个Product类和一个ProductFactory类。Product类表示产品目标,它完成了NSCopying协议以支持仿制。ProductFactory类表示产品工厂,它持有一个Product原型实例,并供给一个createProduct办法来创立新的产品实例。在运用示例中,咱们先创立了一个Product原型实例prototype,然后将其设置到ProductFactory实例的productPrototype特点中。咱们运用ProductFactory实例的createProduct办法来创立新的产品实例product1product2,它们分别仿制了prototype的特点,并进行了修正。

原型形式与制作者形式的结合运用

制作者形式用于创立杂乱目标,它经过分进程的办法来构建目标,而原型形式能够用于快速仿制已有的目标实例,使得在制作目标时愈加高效。在这种结合运用的状况下,制作者形式一般用于创立原型实例,并运用原型实例的仿制办法创立新的目标实例。这种结合运用能够进步目标的创立功率和灵敏性。

例如,在一个游戏中,咱们或许需求创立多种不同类型的人物,并依据用户的需求动态选择人物类型和特点。在这种状况下,咱们能够运用制作者形式来创立原型实例,然后运用原型实例的仿制办法创立新的目标实例。

@interface Character : NSObject <NSCopying>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger level;
@property (nonatomic, assign) CGFloat health;
@property (nonatomic, assign) CGFloat mana;
@end
@implementation Character
- (id)copyWithZone:(NSZone *)zone {
    Character *characterCopy = [[[self class] allocWithZone:zone] init];
    characterCopy.name = self.name;
    characterCopy.level = self.level;
    characterCopy.health = self.health;
    characterCopy.mana = self.mana;
    return characterCopy;
}
@end
@protocol CharacterProtocol <NSObject>
- (void)buildName:(NSString *)name;
- (void)buildLevel:(NSInteger)level;
- (void)buildHealth:(CGFloat)health;
- (void)buildMana:(CGFloat)mana;
@end
@interface CharacterBuilder : NSObject <CharacterProtocol>
@property (nonatomic, strong) Character *character;
@end
@implementation CharacterBuilder
- (instancetype)init {
    self = [super init];
    if (self) {
        self.character = [[Character alloc] init];
    }
    return self;
}
- (void)buildName:(NSString *)name {
    self.character.name = name;
}
- (void)buildLevel:(NSInteger)level {
    self.character.level = level;
}
- (void)buildHealth:(CGFloat)health {
    self.character.health = health;
}
- (void)buildMana:(CGFloat)mana {
    self.character.mana = mana;
}
@end

在运用时:

CharacterBuilder <CharacterProtocol> *characterBuilder = [[CharacterBuilder alloc] init];
[characterBuilder buildName:@"Jane"];
[characterBuilder buildLevel:10];
[characterBuilder buildHealth:100.0];
[characterBuilder buildMana:50.0];
Character *prototype = characterBuilder.character;
Character *characterCopy1 = [prototype copy];
characterCopy1.name = @"Pike";
Character *characterCopy2 = [prototype copy];
characterCopy2.health = 120.0;
NSLog(@"Prototype: %@", prototype);
NSLog(@"characterCopy1: %@", characterCopy1);
NSLog(@"characterCopy2: %@", characterCopy2);

网络恳求

原型形式在网络恳求中也能够运用,一般用于缓存恳求成果以进步应用程序的功能。在某些状况下,应用程序需求从网络中获取很多的数据,并在应用程序中屡次运用相同的数据。假如每次都从网络中获取数据,这将会下降应用程序的功能,而且或许会由于网络延迟导致应用程序的响应速度变慢。在这种状况下,运用原型形式来缓存恳求成果能够进步应用程序的功能。

以下是运用AFNetworking完成原型形式的示例代码:

@interface NetworkRequest : NSObject <NSCopying>
@property (nonatomic, copy) NSString *urlString;
@property (nonatomic, strong) NSDictionary *parameters;
- (void)sendRequestWithSuccess:(void (^)(id responseObject))success
                       failure:(void (^)(NSError *error))failure;
@end
#import "NetworkRequest.h"
#import <AFNetworking/AFNetworking.h>
@implementation NetworkRequest
- (void)sendRequestWithSuccess:(void (^)(id _Nonnull))success failure:(void (^)(NSError * _Nonnull))failure {
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    [manager
     GET:self.urlString
     parameters:self.parameters
     progress:nil
     success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        if (success) {
            success(responseObject);
        }
    }
     failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        if (failure) {
            failure(error);
        }
    }];
}
- (id)copyWithZone:(NSZone *)zone {
    NetworkRequest *copy = [[[self class] allocWithZone:zone] init];
    copy.urlString = self.urlString;
    copy.parameters = self.parameters;
    return copy;
}
@end

然后,创立一个NetworkRequestCache目标,用于缓存已经发送过的网络恳求。

@interface NetworkRequestCache : NSObject
/// 缓存信息
@property (nonatomic, strong) NSMutableDictionary *requestCache;
+ (instancetype)sharedCache;
/// 缓存
- (void)cacheRequest:(NetworkRequest *)request;
- (NetworkRequest *)getCacheRequestForUrlString:(NSString *)urlString parameters:(NSDictionary *)parameters;
@end
@implementation NetworkRequestCache
+ (instancetype)sharedCache {
    static NetworkRequestCache *shareInstance = nil;
    static dispatch_once_t once_token;
    dispatch_once(&once_token, ^{
        shareInstance = [[NetworkRequestCache alloc] init];
    });
    return shareInstance;
}
- (instancetype)init {
    self = [super init];
    if (self) {
        self.requestCache = [NSMutableDictionary dictionary];
    }
    return self;
}
- (NSString *)cacheKeyForUrlString:(NSString *)urlString parameters:(NSDictionary *)parameters {
    NSMutableString *cacheKey = [NSMutableString stringWithString:urlString];
    NSArray *keys = [[parameters allKeys] sortedArrayUsingSelector:@selector(compare:)];
    for (NSString *key in keys) {
        id value = parameters[key];
        [cacheKey appendFormat:@":%@%@", key, value];
    }
    return [cacheKey copy];
}
- (void)cacheRequest:(NetworkRequest *)request {
    NSString *cacheKey = [self cacheKeyForUrlString:request.urlString parameters:request.parameters];
    [self.requestCache setObject:request forKey:cacheKey];
}
- (NetworkRequest *)getCacheRequestForUrlString:(NSString *)urlString parameters:(NSDictionary *)parameters {
    NSString *cache = [self cacheKeyForUrlString:urlString parameters:parameters];
    NetworkRequest *cacheRequest = [self.requestCache objectForKey:cache];
    if (cacheRequest) {
        return [cacheRequest copy];
    } else {
        return nil;
    }
}
@end

其间:

- (NSString *)cacheKeyForUrlString:(NSString *)urlString parameters:(NSDictionary *)parameters {
    NSMutableString *cacheKey = [NSMutableString stringWithString:urlString];
    NSArray *keys = [[parameters allKeys] sortedArrayUsingSelector:@selector(compare:)];
    for (NSString *key in keys) {
        id value = parameters[key];
        [cacheKey appendFormat:@":%@%@", key, value];
    }
    return [cacheKey copy];
}

cacheKeyForUr1String:parameters: 办法依据 URL 字符串和参数生成缓存键。它创立一个可变字符串,初始化为 URL 字符串,并按排序顺序附加每个参数键值对。缓存键的格式为urlString:keyivaluel:key2value2:..该办法回来缓存键字符串的副本。

- (void)cacheRequest:(NetworkRequest *)request {
    NSString *cacheKey = [self cacheKeyForUrlString:request.urlString parameters:request.parameters];
    [self.requestCache setObject:request forKey:cacheKey];
}

cacheRequest: 办法承受一个 NetworkRequest 目标作为参数,并将其缓存起来。它调用 cacheKeyForUrlString:parameters: 办法生成该恳求的缓存键,并将恳求目标与缓存键相关起来存储在 _requestCache 字典中。

- (NetworkRequest *)getCacheRequestForUrlString:(NSString *)urlString parameters:(NSDictionary *)parameters {
    NSString *cache = [self cacheKeyForUrlString:urlString parameters:parameters];
    NetworkRequest *cacheRequest = [self.requestCache objectForKey:cache];
    if (cacheRequest) {
        return [cacheRequest copy];
    } else {
        return nil;
    }
}

getRequestForUrlString:parameters: 办法依据供给的 URL 字符串和参数检索缓存的恳求目标。它运用与 cacheRequest: 办法相同的逻辑生成缓存键。然后在 _requestCache 字典中查找与缓存键相关的恳求目标。假如找到缓存的恳求目标,则运用 copy 办法回来恳求目标的副本,以保证原始的缓存恳求目标坚持不变。假如未找到缓存的恳求,则回来 nil

运用:

NetworkRequest *prototypeRequest = [[NetworkRequest alloc] init];
NSString *urlString = @"https://api.example.com/data";
NSDictionary *parameters = @{@"param1": @"value1", @"param2": @"value2"};
prototypeRequest.urlString = urlString;
prototypeRequest.parameters = parameters;
// 仿制
NetworkRequest *newRequest = [prototypeRequest copy];
newRequest.parameters = @{@"param3": @"value3"};
[newRequest sendRequestWithSuccess:^(id  _Nonnull responseObject) {
    NSLog(@"Request succeeded with response: %@", responseObject);
} failure:^(NSError * _Nonnull error) {
    NSLog(@"Request failed with error: %@", error);
}];
// 将恳求目标缓存起来
[[NetworkRequestCache sharedCache] cacheRequest:newRequest];
// 获取缓存恳求数据
NetworkRequest *networkRequest = [[NetworkRequestCache sharedCache]
getCacheRequestForUrlString:urlString parameters:parameters];

总体而言,NetworkRequestCache 类供给了一种依据 URL 字符串和参数缓存和检索 NetworkRequest 目标的机制。这样能够重复运用恳求目标,并防止重复创立和装备网络恳求目标。缓存功能有助于进步功能和功率,特别是在屡次运用相同的 URL 和参数进行恳求的状况下。

6 制作者形式(Builder Pattern)

24种设计模式代码实例学习(二)创建型模式

制作者形式(Builder Pattern)能够将目标的构建进程与其表示别离,然后使相同的构建进程能够创立不同的表示。这种形式一般用于需求杂乱目标的构建进程,而且需求将构建进程的细节躲藏起来,以便于用户只需求关怀所需的成果即可。

在制作者形式中,一般会界说一个Builder接口或许笼统类,其间包含了一系列构建杂乱目标所需的办法。详细的构建器类完成了Builder接口或笼统类,并依据实践状况来完成每个办法,终究构建出一个详细的产品目标。一起,制作者形式还界说了一个Director类,用于协调Builder目标的构建进程,而且阻隔客户端和产品目标的直接联系。

制作者形式一般适用于以下场景:

  1. 当创立一个杂乱目标时,需求分进程地进行创立,且需求操控每个进程的顺序和内容时,能够运用制作者形式。
  2. 当需求创立的目标具有杂乱的内部结构时,能够运用制作者形式来阻隔杂乱的目标创立进程,然后使其更易于理解和保护。
  3. 当需求创立多个具有类似特点的目标时,能够运用制作者形式来防止重复的结构代码,而且能够更容易地扩展和修正这些目标的结构进程。
  4. 当需求创立一个不可变的目标时,能够运用制作者形式来强制其必须经过结构器来创立,然后防止了目标在创立后被修正的或许性。

比如

举一个详细的比如,比如咱们需求构建一份简历,其间包含个人信息、教育阅历、工作阅历等。假如运用制作者形式,咱们能够将这份简历的构建进程分为多个进程,并由详细的构建器来担任每个进程的完成。这样做的优点是,咱们能够依据不同的需求来构建不同风格的简历,而不需求在客户端代码中重复编写类似的代码。

// Person 类
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *email;
@property (nonatomic, copy) NSString *phone;
@property (nonatomic, copy) NSString *address;
@end
@implementation Person
@end
// Education 类
@interface Education : NSObject
@property (nonatomic, copy) NSString *degree;
@property (nonatomic, copy) NSString *school;
@property (nonatomic, copy) NSString *date;
@end
@implementation Education
@end
// WorkExperience 类
@interface WorkExperience : NSObject
@property (nonatomic, copy) NSString *company;
@property (nonatomic, copy) NSString *position;
@property (nonatomic, copy) NSString *date;
@end
@implementation WorkExperience
@end
// 制作者接口
@protocol ResumeBuilderProtocol <NSObject>
- (void)buildBasicInfoWithName:(NSString *)name email:(NSString *)email phone:(NSString *)phone address:(NSString *)address;
- (void)buildEducationWithDegree:(NSString *)degree school:(NSString *)school date:(NSString *)date;
- (void)buildWorkExperienceWithCompany:(NSString *)company position:(NSString *)position date:(NSString *)date;
- (NSString *)getResult;
@end
// 简历制作者类
@interface ResumeBuilder : NSObject <ResumeBuilderProtocol>
@property (nonatomic, strong) Person *person;
@property (nonatomic, strong) Education *education;
@property (nonatomic, strong) WorkExperience *workExperience;
@end
@implementation ResumeBuilder
- (void)buildBasicInfoWithName:(NSString *)name email:(NSString *)email phone:(NSString *)phone address:(NSString *)address {
    self.person = [Person new];
    self.person.name = name;
    self.person.email = email;
    self.person.phone = phone;
    self.person.address = address;
}
- (void)buildEducationWithDegree:(NSString *)degree school:(NSString *)school date:(NSString *)date {
    self.education = [Education new];
    self.education.degree = degree;
    self.education.school = school;
    self.education.date = date;
}
- (void)buildWorkExperienceWithCompany:(NSString *)company position:(NSString *)position date:(NSString *)date {
    self.workExperience = [WorkExperience new];
    self.workExperience.company = company;
    self.workExperience.position = position;
    self.workExperience.date = date;
}
- (NSString *)getResult {
    NSMutableString *result = [NSMutableString string];
    [result appendFormat:@"Name: %@\n", self.person.name];
    [result appendFormat:@"Email: %@\n", self.person.email];
    [result appendFormat:@"Phone: %@\n", self.person.phone];
    [result appendFormat:@"Address: %@\n", self.person.address];
    [result appendFormat:@"\n"];
    [result appendFormat:@"Education:\n"];
    [result appendFormat:@"Degree: %@\n", self.education.degree];
    [result appendFormat:@"School: %@\n", self.education.school];
    [result appendFormat:@"Date: %@\n", self.education.date];
    [result appendFormat:@"\n"];
    [result appendFormat:@"Work Experience:\n"];
    [result appendFormat:@"Company: %@\n", self.workExperience.company];
    [result appendFormat:@"Position: %@\n", self.workExperience.position];
    [result appendFormat:@"Date: %@\n", self.workExperience.date];
    return result;
}

现在咱们运用 ResumeDirector 类来调用制作者来创立简历,详细代码如下:

// 简历指导者类
@interface ResumeDirector : NSObject
@property (nonatomic, strong) id<ResumeBuilderProtocol> builder;
- (instancetype)initWithBuilder:(id<ResumeBuilder>)builder;
- (NSString *)construct;
@end
@implementation ResumeDirector
- (instancetype)initWithBuilder:(id<ResumeBuilder>)builder {
    if (self = [super init]) {
        _builder = builder;
    }
    return self;
}
- (NSString *)construct {
    [self.builder buildBasicInfoWithName:@"John"
                                   email:@"john@example.com"
                                   phone:@"1234567890"
                                 address:@"123 Main St."];
    [self.builder buildEducationWithDegree:@"Bachelor"
                                     school:@"University"
                                       date:@"2010-2014"];
    [self.builder buildWorkExperienceWithCompany:@"ABC Company"
                                        position:@"Software Engineer"
                                            date:@"2014-2016"];
    return [self.builder getResult];
}
@end

这个 ResumeDirector 类承受一个制作者目标,然后在 construct 办法中按照特定的顺序调用制作者的办法来构建简历。现在咱们能够运用这个 ResumeDirector 来构建简历:

id <ResumeBuilderProtocol> builder = [[ResumeBuilder alloc] init];
ResumeDirector *director = [[ResumeDirector alloc] initWithBuilder:builder];
NSString *str = [director construct];
NSLog(@"builderPattern:%@", str);

输出成果为:

Name: John
Email: john@example.com
Phone: 1234567890
Address: 123 Main St.
Education:
Degree: Bachelor
School: University
Date: 2010-2014
Work Experience:
Company: ABC Company
Position: Software Engineer
Date: 2014-2016

能够看到,这个比如中,Director 类将简历的构建进程封装起来,客户端代码只需求经过 Director 来构建简历,而不必了解详细的制作者目标是怎么构建简历的。 这样,当简历的构建进程发生变化时,只需求修正制作者和指导者类就能够了,而不需求修正客户端代码。这样,能够进步代码的可保护性和扩展性。

项目Demo

24-Design-Patterns

下一篇

24种规划形式代码实例学习(三)结构型形式