如何在Objective-C中创建策略模式?

12

我需要开发一个策略模式,其中有一个主类和其他三个类,我需要使用主类对象引用其他三个类的对象。使用策略模式可以解决这个问题吗?如果可以,请给我提供Objective-C的语法。


这似乎是您现有问题的副本:http://stackoverflow.com/questions/2229026 - Rob Keniger
是的,但我在之前的问题中没有提到策略模式。 - Cathy
2个回答

39

你需要查看Objective-C的协议机制。这里是一个简单的协议,它有一个必需的方法:

@protocol Strategy <NSObject>

@required
- (void) execute;

@end

接下来声明一个符合该协议的类:

@interface ConcreteStrategyA : NSObject <Strategy>
{
    // ivars for A
}
@end

实现类必须提供-execute方法(因为它被声明为@required):

@implementation ConcreteStrategyA

- (void) execute
{
    NSLog(@"Called ConcreteStrategyA execute method");
}

@end

你可以创建一个类似的 ConcreteStrategyB 类,但我不会在这里展示它。

最后,创建一个上下文类,并使用一个属性来维护当前策略。

@interface Context : NSObject
{
    id<Strategy> strategy;
}
@property (assign) id<Strategy> strategy;

- (void) execute;

@end

这里是实现代码。委托给策略的方法名为“-execute”,但实际上可以使用任何方法名。

@implementation Context

@synthesize strategy;

- (void) execute
{
    [strategy execute];
}

@end

现在我将创建几个实例并将它们投入使用:

ConcreteStrategyA * concreteStrategyA = [[[ConcreteStrategyA alloc] init] autorelease];
ConcreteStrategyB * concreteStrategyB = [[[ConcreteStrategyB alloc] init] autorelease];
Context * context = [[[Context alloc] init] autorelease];

[context setStrategy:concreteStrategyA];
[context execute];
[context setStrategy:concreteStrategyB];
[context execute];    

控制台输出显示策略已成功更改:

2010-02-09 19:32:56.582 Strategy[375:a0f] Called ConcreteStrategyA execute method
2010-02-09 19:32:56.584 Strategy[375:a0f] Called ConcreteStrategyB execute method
请注意,如果协议没有指定@required,则该方法是可选的。在这种情况下,上下文需要检查策略是否实现了该方法:
- (void) execute
{
    if ([strategy respondsToSelector:@selector(execute)])
        [strategy execute];
}

这是一个常见的Cocoa设计模式,称为委托。有关Cocoa中委托和其他设计模式的更多信息,请参见此处


非常感谢您的回复,我有一个疑问,这里的“setStrategy”关键字在您创建的实例中代表什么。 - Cathy
-setStrategy: 方法是由 @synthesize 指令自动生成的。如果属性声明为 retain,它会负责执行 retain/release(但你仍然需要在 -dealloc 中释放它)。但如果属性声明为 assign,它将作为弱引用进行简单赋值,假设被分配的实例已经存在并在其他地方进行管理。在这种情况下,它不应该被释放。...事实上,我要把我的答案改成这个第二种形式。 - Jon Reid
1
通常你会在使用 @protocol 的同一个头文件中声明它。策略模式在Cocoa中非常常见,尽管它经常被称为Delegate。 - Rob Napier
3
不要使用id<Strategy, NSObject>,通常更好的做法是让Strategy本身继承自<NSObject>协议: "@protocol Strategy <NSObject>" 这几乎总是你想要的,并且简化了使用方法。 - Rob Napier
Cathy,你之所以出现“重复声明”的问题,是因为你声明了两次。最好将协议放在头文件中。 - Jon Reid
显示剩余5条评论

1

这里有一个更具体的例子。你可以将每个项目放在单独的文件中。我将它们全部放在一个文件中,以便更容易理解。

//  main.m
//  StrategyWikipediaExample
//
//  Created by steve on 2014-07-08.
//  Copyright (c) 2014 steve. All rights reserved.
//

#import <Foundation/Foundation.h>

/**
 Equivalent to Java Interface
 All concrete Strategies conform to this protocol
 */
@protocol MathOperationsStrategy<NSObject>
- (void)performAlgorithmWithFirstNumber:(NSInteger)first secondNumber:(NSInteger)second;
@end

/**
 Concrete Strategies. 
 Java would say they "Extend" the interface.
 */

@interface AddStrategy : NSObject<MathOperationsStrategy>
@end
@implementation AddStrategy
- (void)performAlgorithmWithFirstNumber:(NSInteger)first secondNumber:(NSInteger)second
{
    NSInteger result = first + second;
    NSLog(@"Adding firstNumber: %ld with secondNumber: %ld yields : %ld", first, second, result);
}
@end

@interface SubtractStrategy : NSObject<MathOperationsStrategy>
@end
@implementation SubtractStrategy
- (void)performAlgorithmWithFirstNumber:(NSInteger)first secondNumber:(NSInteger)second
{
    NSInteger result = first - second;
    NSLog(@"Subtracting firstNumer: %ld with secondNumber: %ld yields: %ld", first, second, result);
}
@end

@interface MultiplyStrategy : NSObject<MathOperationsStrategy>
@end
@implementation MultiplyStrategy
- (void)performAlgorithmWithFirstNumber:(NSInteger)first secondNumber:(NSInteger)second
{
    NSInteger result = first * second;
    NSLog(@"Multiplying firstNumber: %ld with secondNumber: %ld yields: %ld", first, second, result);
}
@end

@interface Context : NSObject
@property (weak, nonatomic)id<MathOperationsStrategy>strategy; // reference to concrete strategy via protocol
- (id)initWithMathOperationStrategy:(id<MathOperationsStrategy>)strategy; // setter
- (void)executeWithFirstNumber:(NSInteger)first secondNumber:(NSInteger)second;
@end
@implementation Context
- (id)initWithMathOperationStrategy:(id<MathOperationsStrategy>)strategy
{
    if (self = [super init]) {
        _strategy = strategy;
    }
    return self;
}
- (void)executeWithFirstNumber:(NSInteger)first secondNumber:(NSInteger)second
{
    [self.strategy performAlgorithmWithFirstNumber:first secondNumber:second];
}
@end


int main(int argc, const char * argv[])
{

    @autoreleasepool {
        id<MathOperationsStrategy>addStrategy = [AddStrategy new];
        Context *contextWithAdd = [[Context alloc] initWithMathOperationStrategy:addStrategy];
        [contextWithAdd executeWithFirstNumber:10 secondNumber:10];

    }
    return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接