3

出于多种原因,我想将数据与多个类中的实现分开。

例如,一个原因是我有几个不同的菜单屏幕来显示文本。我希望有一个类在一个地方列出所有菜单的所有文本,然后在初始化时从该类中读取不同的菜单对象。

这样,每当我想进行更改时,我都知道文本变量的确切位置,如果我愿意,我可以一次更改一堆。

我想在很多不同的方式中使用相同的原理,例如,设置各种 UIView 的颜色和 alpha 值;将它们全部放在一个地方将使我能够协调它们的设置并非常轻松地进行小调整。

除了这些原因之外,我正在与一个由其他开发人员组成的小团队合作,如果我们都知道我们将此类信息存储在一个地方,则更容易理解彼此的代码。

所以基本上我想要一个大的 UberData 类,每个其他类都可以读取和写入。

据我所知,做到这一点的唯一方法是使每个需要的变量成为一个属性,所以我基本上会拥有一个包含大量属性的大型无方法类。但据我了解,这有点违反 OO 规则,因为尽可能多的类应该隐藏它们的内部结构。更不用说整个事情看起来真的很笨拙。

所以问题是:有比拥有一百万个属性的类更好的方法吗?从面向对象的角度来看,这样做是否合适?

4

5 回答 5

2

一个UberData大类(实际上,如果您正在考虑属性,您的意思是该类的一个实例)是错误的方法。

菜单字符串和视图颜色有什么关系?没有什么。因此它们不属于同一类。

字符串

对于您的菜单字符串,请查看NSLocalizedString并创建一个字符串文件。您可以创建一个CommonStrings包含所有调用的类NSLocalizedString

@interface CommonStrings : NSObject

+ (NSString *)open;
+ (NSString *)save;
// etc.

@end

@implementation CommonStrings

+ (NSString *)open {
    return NSLocalizedString(@"open", @"menu item title for opening a file");
}

+ (NSString *)save {
    return NSLocalizedString(@"save", @"menu item title for saving a file");
}

// etc.

@end

这种方法意味着您只在一个地方编写@"open",然后[CommonStrings open]在需要(本地化)字符串时引用。编译器会检查您是否拼写[CommonStrings open]正确,这很好。

但是,将其分解为多个帮助器(一个用于应用程序的每个独立部分),而不是为整个应用程序提供一个巨大的帮助器,仍然可能更好。如果你使用一个巨大的包罗万象的类,那么编译你的应用程序需要更长的时间,因为每次你在这个类中添加或删除一个方法时都必须重新编译很多东西。

UIView 颜色

首先,观看WWDC 2012WWDC 2013的外观定制视频并继续阅读UIAppearance。也许您可以使用它来自定义应用程序的颜色。

如果这还不够,UIColor请为您的应用颜色创建一个类别:

@interface UIColor (MyApp)

+ (UIColor *)MyApp_menuBackgroundColor;
+ (UIColor *)MyApp_menuTextColor;
// etc.

@end

@implementation UIColor (MyApp)

+ (UIColor *)MyApp_menuBackgroundColor {
    return [self colorWithPatternImage:[UIImage imageNamed:@"menuBackgroundPattern"]];
}

+ (UIColor *)MyApp_menuTextColor {
    return [self colorWithWhite:0.0 alpha:1.0];
}

// etc.

@end

同样,为应用程序的不同部分设置多个帮助器类别可能会更好,因此在添加或删除类别方法时不必重新编译。

于 2013-09-13T20:44:19.713 回答
2

首先,忘记“礼节”。正确的是有效、可靠、高效、易于理解和易于维护。坚持“面向对象原则”,虽然在某种程度上是一个有价值的目标,但不应该让你做尴尬、容易出错和低效的事情。几乎任何“编程规则”都可以按照字面意思进行。

从多个角度来看,拥有数十个(或数百个)属性是笨拙且难以维护的,因此这基本上是行不通的。

更合适的是有一些可查询的接口返回你想要的值。“正确的”方案通常取决于数据的特征,但可以有一种方法,例如,有一个简单的switch语句或“if ladder”,在给定值 X 的情况下返回值 Y(其中 X 是,例如,枚举值) .

人们也可以拥有实际上或概念上是 NSDictionary 的内容,您可以使用字符“键”值进行查询。

它的一个变体是属性列表,它允许您描述数据文件中的数据,而不必将其编码到源代码中。如果更适合您的设计和习惯,您也可以将数据放入 JSON 文件中。

还有一些我过去使用过的其他方案,我现在还没有想到。

添加:

好的,我将举一个“宏”和“外部变量”基本上不可能的例子。在某些情况下,在我处理的一些代码中,有些对象包含有关特定事件的信息。可能有 50 种不同的事件类别,每一种都有不同的属性集(子类别、显示颜色、各种条件的文本等)。

当然,可以有数百个声明的常量,样式为“kXyzCategoryAbcSubCategory”、“kXyzCategoryAbcColorMode”、“kXyzCategoryAbcTitleText”等,但维护是一个 PITA,使用中的拼写错误是例行公事。另外,它并不是真正可用的,因为您不能从对象中获取“类别”并“索引”属性。

相反,我使用以下两种方案之一:

  1. 一组可调用接口,您在其中传入常量“kXyzCategoryAbc”(或从对象中动态提取的类别值)并调用多个方法之一——xyzSubCategory、xyzColorMode、xyzTitleText——然后该方法返回所需的数据。
  2. 一个可调用的接口,您在其中传递类别值并返回一个 NSArray(带有键,例如“subCategory”、“colorMode”和“titleText”)。

这两种技术都工作得很好,尽管根据情况可能会首选其中一种。两者都允许您将数据维护为某种表格,并允许您使用相同的常量来获取多个值,而当您向其中一个类别对象添加一个新属性时,必须引入 50 个新常量。

于 2013-09-13T20:44:37.293 回答
1

是的,对单个共享配置对象的常见响应是单例模式。简而言之,类方法知道如何创建或返回类的实例,并且该实例知道如何配置所需的东西。我想你会通过搜索找到很多堆栈溢出的结果,尽管我的 Objective-C 单例应该是什么样子?是一个简单的例子。

除此之外,我鼓励您研究本地化在 iOS 中的工作原理——您最初提到的菜单标题和事物来源的问题是本地化库以可扩展的方式为您解决的问题。

您可能还想查看属性列表(又名 plists),它是一个结构化数据文件,可以在 iOS 应用程序中轻松读取。

于 2013-09-13T20:36:34.450 回答
1

您根本不应该为此使用课程。对代码内部常量使用宏,对可以改变的值使用外部变量。

注意:用户可见的字符串应该通过本地化文件完成 - 请参阅其他答案。

当涉及到常量时,我​​通常有一个CPConstants.h(CP 是我的类前缀)文件,它看起来像这样:

#define kCLConstant1 42
#define kCLColorConstant [UIColor blackColor]

等等。

如果您需要更改值,请首先创建一个CLConstants.m文件,如下所示:

#import "CLConstants.h"

int some_global_var = 42;
UIColor* some_global_changable_color = [UIColor blackColor];

等等。然后,在 中CLConstants.h,为您在 中声明的每个变量添加这样的一行CLConstants.m

extern type varname;

现在所有包含/导入的文件CLConstants.h都可以使用和更改这些变量,并且更改将对项目中的所有其他文件可见。

于 2013-09-13T20:43:52.840 回答
0

在跨多个控制器共享数据时,您基本上有两种选择:

1) 使用singleton

在这种情况下,singleton数据模型听起来很合适。(singleton数据模型是由多个控制器使用的数据模型的单个共享实例。)

但是,正如您所提到的,您确实可能会通过这样做最终得到一个具有许多属性的类……但是,这本身并没有错。只是选择你在单例上的属性......如果它没有被多个控制器共享,它可能不应该在那里。

请参阅 Apple 的文档,了解如何在此处创建此类文档。

这是另一篇关于iOS 中单例的 SO 帖子。

2) 使用一个头文件.h.pch

通常,这是使用static constant变量完成的。我想你可以通过static变量来做到这一点,以便你可以编辑它们,但至少我最后一次检查,编译器可能会给出不正确的unused变量警告。

于 2013-09-13T20:32:15.767 回答