2

我正在制作一个计算器应用程序来学习 Objective-C 并且可能会提高我的 OO 设计技能。为了做更多类似 MVC 的事情,我将实际的计算器代码与视图控制器分开。对于每个动作,视图控制器所做的几乎所有事情都是告诉“模型”执行针对该动作的操作。

问题是,这给了我一堆方法,这些方法基本上什么都不做,只是将动作转发给模型,如下所示:

- (IBAction)clearAll:(id)sender {
    [self.model clearAll];
}

- (IBAction)clearDisplay:(id)sender {
    [self.model clearDisplay];
}

- (IBAction)clearMemory:(id)sender {
    [self.model clearMemory];
}

- (IBAction)storeMemory:(id)sender {
    [self.model storeMemory];
}

- (IBAction)addMemory:(id)sender {
    [self.model addMemory];
}

- (IBAction) subtractMemory:(id)sender {
    [self.model subtractFromMemory];
}

- (IBAction)recallMemory:(id)sender {
    [self.model recallMemory];
}

到目前为止,Objective-C 在动态转发消息方面似乎非常灵活,而且这些方法非常相似,看起来很容易自动化。他们真的必须在那里吗?还是有一种不那么重复的方法来告诉控制器只将某些消息传递给模型(理想情况下,同时剥离senderarg)?

我一直在寻找并尝试使用选择器和 的一些东西,但是通过删除所有让我将按钮连接到操作NSInvocation的标记,这似乎会使 Interface Builder 混乱。(IBAction)(在这些情况下,如果视图不必知道或关心控制器只是转发到模型,我会更喜欢。)

那么,有没有更少重复和/或骇人听闻的方式?还是不值得麻烦?(或者这首先是一个坏主意?或者这是试图让模型做太多事情?或者......)

4

2 回答 2

4

您可以使用语言的动态特性。

来自 Objective-C 运行时编程文档

当一个对象因为没有与消息中的选择器匹配的方法而无法响应消息时,运行时系统会通过发送一个forwardInvocation.

因此,在您的情况下,您可以按如下方式实现前向调用方法

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    if ([self.model respondsToSelector:[anInvocation selector]])
        [anInvocation invokeWithTarget:self.model];
    else
        [super forwardInvocation:anInvocation];
}

注意 您还必须统一您的方法签名。删除sender参数或将其添加到模型的方法中,否则respondsToSelector将返回NO并且不会调用该方法。

在这种情况下forwardInvocation,它将充当调度程序,它将尝试将您的控制器未实现的每条消息发送到self.model对象。如果这没有响应选择器,它将调用super,很可能导致无法识别的选择器异常。

我个人觉得它非常优雅,即使您最好确切地知道自己在做什么并且绝对不要过度使用此类功能。

于 2012-12-31T01:00:04.300 回答
4

您可以按照 Gabriele 的建议进行操作,这无疑是 ObjC 的动态示例,但您最好避免使用它。正如 Gabriele 所说,您最好确切地知道自己在做什么,并且绝对不要过度使用此类功能。这通常表明这样的功能可能比它的价值更麻烦。

现实情况是,您的计算器应用程序是一个非常人为的设计,目的是为了将模型-视图-控制器模式固有的分离带回家。正如您所说,这是一个学习应用程序。

实际上,没有任何应用程序如此简单。如果有的话,您很少会拥有一个按钮字段,控制层会在其中盲目地将所述功能转发给模型。

取而代之的是,该控制层中将存在各种业务逻辑方式,这些逻辑可以完成从自动化各种操作到验证(可能通过查询模型)到更新 UI 状态以响应各种操作的所有事情。

很可能此代码将在项目的早期出现,因此通用转发机制将很快变得完全未使用。

同样,当涉及到调试时,这样的转发机制会变成充满痛苦的漏斗。您不再有一个具体的点来放置断点,但现在必须添加条件。您也没有一种简单的方法可以找到所有可能调用或实现特定方法的位置。同样,它使跟踪控制流变得更加困难。

如果您确实发现自己有大量重复的样板代码,那么这更像是您的架构可能存在缺陷的迹象,而不是您需要注入一个漂亮的动态机制来减少重复性的迹象。

同样,如果您要继续充实您的计算器应用程序,与应用程序中的所有其他功能相比,您将花费多少编码时间来执行这些重复的方法?可能非常少,并且由于它们的简单性和调试方便,所述重复方法不太可能会产生任何重大的维护成本,而一些巧妙的动态技巧(这非常酷,我鼓励你在其他情况下探索)几乎可以保证需要“嗯。我在想什么?!” 片刻之后。

于 2012-12-31T01:30:46.527 回答