什么是制作 Objective-C 类的不可变和可变版本最有效的方式?

9
假设我正在创建一个表示分数的Objective-C类,并希望创建不可变和可变版本。根据Foundation框架中的模式,您可能会在不可变版本中看到fractionByAddingFraction:方法,在可变版本中看到addFraction:方法。
我遇到的悖论是如何在两个类之间仅包含一次分数添加逻辑。似乎不可变的fractionByAddingFraction:方法需要了解(并利用)可变的addFraction:方法,以避免代码重复,但是在不可变类的实现中包含可变方法意味着它们可能会在不可变对象上调用,这与设计思路背道而驰。
欢迎进一步阐述这个简化示例!

4
您的示例似乎表示一个“值对象”,因此我认为您不应该为其提供可变版本。请使其成为不可变的。 - Jordão
@Jordão 对于这个特定的例子,你是完全正确的。我只是觉得这是解释我对概念的困惑最简单、最简洁的方式(因为它涉及到更复杂的类)。无论如何,你有什么想法吗? - user1385983
2个回答

3

您的方法是正确的(如果您确实需要可变子类,则应避免使用,除非您真正需要它)。我不太清楚混淆发生在哪里。您最容易使用fractionByAddingFraction:来实现addFraction:。这可能有点低效,但这是最合理的方向。像这样:

- (void)addFraction:(Fraction *)anotherFraction {
   Fraction *newFraction = [self fractionByAddingFraction:anotherFraction];
   self.internalStuff = newFraction.internalStuff;
}

但通常情况下,您可能会使用一些私有_GetInternalStuffByAddingInternalStuffs()函数更有效地处理此问题,这两个类都会使用该函数。


谢谢您的建议。我之前避免使用第一种方法,正如您所提到的,每次更改其任何组件都创建一个新对象可能效率低下。您提出的将其作为私有函数的想法似乎是一个不错的替代方案,但我想知道这个函数最好放在哪里,以便两个类的实现都可以访问它,但其他任何东西都无法访问(这排除了将其放在头文件中的可能性)。 - user1385983
通常通过使用私有头文件,如Fraction+Private.h来完成此操作。请注意,如果是这种情况,您仍然可能希望考虑放弃可变形式。请注意NSNumber没有可变形式。除非您非常快地创建许多它们,否则值对象的简单性和线程安全性使它们非常具有吸引力。即使您很快地创建它们,也可以缓存其中的一些。据我所知,NSNumber将整数-1-12作为单例缓存在内存中。您可以最轻松地使用不可变对象执行此类缓存。但是,当您需要可变性时,请参照上述方法进行处理。 - Rob Napier
请记住,您可以查看底层CoreFoundation对象的实现方式:http://opensource.apple.com/source/CF/CF-635/CFArray.c。 - Rob Napier

0
Foundation的集合主要有一种实现方式:只有一个实现,它是NSMutableFoo的子类,并且具有私有的可变性标志。这意味着客户端代码无法测试特定对象是否可变,但这从来不是一个好主意,除了调试和断言之外。

我理解的是否正确,'NSFoo'将是'MutableFoo'的子类,而'MutableFoo'将包含两个类的所有方法,但基于私有标志限制其不可变子类使用可变方法? - user1385983
NSFoo并不是NSMutableFoo的子类。它们之间的关系通常是NSFooNSMutableFooNSCFFoo,其中NSCFFoo是一个具体的实现。NSCFFoo的可变方法会检查可变标志,并在其为不可变时抛出异常。(由于免费桥接的存在,实际情况比这更复杂,但如果没有涉及CF,就是这样工作的。)对于您自己的类,您可能会将具体的子类命名为XYConcreteFoo - Jens Ayton

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