【问题标题】:Understanding Model-View-Controller了解模型-视图-控制器
【发布时间】:2016-04-11 19:15:49
【问题描述】:

我正在自学Objective C,并且我正在尝试使用模型-视图-控制器的概念来进步。我的问题是:在模型类中创建数据的正确方法是什么?我有一个例子来说明我的意思,它有点长,但一个非常简单的问题。

以下代码来自我刚刚完成的一个记忆游戏。它工作正常,我已将数据与视图控制器分开,因此我有一个单独的类来保存卡片的图像。这是那堂课。顺便说一句,这一切都有效,对我的问题来说唯一重要的是声明了一个数组,因此请随意跳过大部分复制图像和随机化的代码。无论如何,这里是:

导入“卡片.h”

@实现卡片

- (NSMutableArray*) createCardImages{
    
    NSMutableArray *Fruits = [[NSMutableArray alloc] initWithObjects:
                       
                       [UIImage imageNamed:@"Apple.png"],
                       [UIImage imageNamed:@"Banana.png"],
                       [UIImage imageNamed:@"Pear.png"],         
                       [UIImage imageNamed:@"Strawberry.png"],
                       [UIImage imageNamed:@"Lemon.png"],
                       [UIImage imageNamed:@"Orange.png"],        
                       [UIImage imageNamed:@"Pineapple.png"],
                       [UIImage imageNamed:@"Kiwi.png"],
                       [UIImage imageNamed:@"Prune.png"],
                       [UIImage imageNamed:@"Peach.png"],
                        nil];

    NSUInteger tileCount = [Fruits count];
    
    for (NSUInteger i = 0; i < tileCount; ++i) {
        NSInteger nElements = tileCount - i;
        NSInteger n = (arc4random() % nElements) + i;
        [FullList exchangeObjectAtIndex:i withObjectAtIndex:n];
    }
    
        NSArray *Shortlistprep1 = [Fruits subarrayWithRange:NSMakeRange(0, 10)];
    
        NSArray *ShortListprep2=[Shortlistprep1 arrayByAddingObjectsFromArray:Shortlistprep1];

    
        NSMutableArray *ShortList = [[NSMutableArray alloc]init];
    
        ShortList = [NSMutableArray arrayWithArray:ShortListprep2];
    
    return ShortList;
}

- (NSMutableArray*) shuffleCards{
    
    
   NSUInteger cardCount = [self.cardImages count];
    
    NSMutableArray *count = [[NSMutableArray alloc]init];
    
    for (int tileID = 0; tileID < cardCount; tileID++){
        [count addObject:[NSNumber
                                       numberWithInt:tileID]];
    }
    
        return count;
}

然后我从视图控制器调用此方法,一切正常。 接下来,我想添加一个不同的图像列表,以便在玩游戏时可以选择不同的图像集。比如:

NSMutableArray *Clothes = [[NSMutableArray alloc] initWithObjects:
                           
                       [UIImage imageNamed:@"Hat.png"],
                       [UIImage imageNamed:@"Coat.png"],
                       [UIImage imageNamed:@"Gloves.png"],         
                       [UIImage imageNamed:@"Trousers.png"],
                       [UIImage imageNamed:@"Socks.png"],
                       [UIImage imageNamed:@"Sweater.png"],        
                       [UIImage imageNamed:@"T-shirt.png"],
                       [UIImage imageNamed:@"Shirt.png"],
                       [UIImage imageNamed:@"Jumper.png"],
                       [UIImage imageNamed:@"Underwear.png"],
                        nil];

我知道我可以为每个新集合复制整个方法,如下所示:

  - (NSMutableArray*) createCardImagesClothes{
        
         NSMutableArray *Clothes = [[NSMutableArray alloc] initWithObjects:
                           
                       [UIImage imageNamed:@"Hat.png"],
                       [UIImage imageNamed:@"Coat.png"],
                       [UIImage imageNamed:@"Gloves.png"],         
                       [UIImage imageNamed:@"Trousers.png"],
                       [UIImage imageNamed:@"Socks.png"],
                       [UIImage imageNamed:@"Sweater.png"],        
                       [UIImage imageNamed:@"T-shirt.png"],
                       [UIImage imageNamed:@"Shirt.png"],
                       [UIImage imageNamed:@"Jumper.png"],
                       [UIImage imageNamed:@"Underwear.png"],
                        nil];

        NSUInteger tileCount = [FullList count];
        
        for (NSUInteger i = 0; i < tileCount; ++i) {
            NSInteger nElements = tileCount - i;
            NSInteger n = (arc4random() % nElements) + i;
            [FullList exchangeObjectAtIndex:i withObjectAtIndex:n];
        }
        
            NSArray *Shortlistprep1 = [FullList subarrayWithRange:NSMakeRange(0, 10)];
        
            NSArray *ShortListprep2=[Shortlistprep1 arrayByAddingObjectsFromArray:Shortlistprep1];
    
        
            NSMutableArray *ShortList = [[NSMutableArray alloc]init];
        
            ShortList = [NSMutableArray arrayWithArray:ShortListprep2];
        
        return ShortList;
    }

如果这是前进的道路,我很乐意这样做,我只是想学习最佳实践。我觉得应该有一种方法可以只编写一个方法,然后在 h 文件上声明属性:

   #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    
    @interface Cards : NSObject
    
    // Create Cards
    @property NSMutableArray *cardImages;
    @property NSMutableArray *shuffledCards;

   @propery NSMutableArray *Fruits;

   @propery NSMutableArray *Clothes;

   @propery NSMutableArray *SomeOtherSet;

   @propery NSMutableArray *SomeOtherSet;

   @propery NSMutableArray *SomeOtherSet;
    
    - (NSMutableArray*) createCardImages;
    - (NSMutableArray*) shuffleCards;

    @end

然后我要做的就是修改我的方法以接受 NSMutableArray 类型的参数,如下所示:

- (NSMutableArray*) createCardImages:(NSMutableArray);

只需使用代表我要加载的任何集合的参数调用该方法。

我的问题是一个相当基本的问题,即我将在哪里实际将信息加载到这些数组中。 “m”文件上没有“main”或“viewDidLoad”方法来实际将数据(在本例中为图像)加载到数组中。

这有意义吗?

感谢阅读。

【问题讨论】:

  • 无论您的目标是什么,您可能需要注意 model 层仅存储/恢复/管理数据(例如 -create...),但任何进一步的交互随着数据在逻辑上在 controller 层(例如-shuffle...)上启动,因此为了洗牌,在NSMutableArray 上创建一个类别会更合理——但显然这是基于主观仅供参考 - 因为没有严格的规则说明为什么要这样做。
  • @holex - 实际上,在标准 MVC 中,模型层封装了数据以及与该数据相关的所有操作。我会打电话给shuffle 绝对是在一副纸牌上的正常操作,它应该放在模型层中。控制器层仅包含将模型映射到视图所需的功能。
  • @fishinear,实际上这是基于主要意见的,因为开发人员决定 model-layer 的结束位置以及 controller-layer 的开始位置; 这里唯一的规则是每一层都必须是可替换的,而不影响其他两层;像例如在 model 上,应用程序最初将所有内容存储在本地 plist 文件中,但出于某种原因,您需要用 CoreData + iCloud 替换存储引擎,因此这种更改一定不会影响 控制器view 层——如果它影响并且它们不能相同,那么当前的 MVC 模式是完全错误的。
  • @holex - 所有设计都是基于意见的。您所说的“唯一规则”实际上是所有OO设计的正常数据封装规则。但是成熟的 MVC 设计模式为每一层分配了特定的角色。但是这里不是讨论这个的地方。如果您想进一步讨论,请随时 PM 我。
  • 感谢孔我明白你在说什么。我将考虑在 NSMutableArray 上创建一个类别,而不是我以前做过的事情。我如何将图像加载到卡片类的数组中?

标签: objective-c arrays model-view-controller


【解决方案1】:

可以说您不应该公开可变数组,您的模型应该是更好的数据容器并实际操作数据。通过返回一个数字数组进行改组实际上并不会改变数据模型,它应该。

此外,如果您的数据模型处理数据管理,而不是显式创建,它会更简洁。如果您有一个工厂类,可以要求它生成一组图像并为您的Cards 类提供这些图像来处理您会更好地分离您的关注点。您可以将类方法添加到Cards,或者添加一个使用枚举来决定使用哪些图像的实例方法,但图像对于将这些图像作为卡片进行管理是一个单独的问题。

因此,当创建视图控制器以管理游戏时,它会向工厂类询问指定图像类型的 Cards 实例。一旦拥有 Cards 实例,它就可以对其进行洗牌,然后在指定的索引或网格位置请求卡片。

索引和网格位置是有趣的部分,因为不同的游戏类型可能会有所不同。不同的游戏类型都可以使用同一组卡片(尽管使用不同的Cards 实例),但以不同的方式索引这些卡片。为了支持这一点,最好为卡片提供一个简单的索引,并允许了解游戏逻辑的控制器决定如何使用该索引系统。

在上述所有情况下,控制器从不将卡片作为数组访问。接口类似,但所有交互都与Cards 进行,并且该类隐藏了其内部结构。在内部它会使用一个数组,但它可以使用其他东西,控制器不应该关心。

【讨论】:

  • 据我所知,这是一个非常好的答案,但对于我现在的水平来说有点太抽象了。我将不得不研究它,我会的。我意识到我正在朝着正确的方向前进,但仍处于开始阶段。我可以看到您对课堂上未修改的数据的看法。您能否简单地解释一下如何将图像实际加载到@property NSMutableArray *fruits;在实际的 Card m 文件中?如果我在视图控制器中执行此操作,我要做的就是cards.fruits = 并在 viewDidLoad 下加载一组图像,但卡片类中没有“viewDidLoad”。
  • 触发器在控制器中启动整个过程,它调用工厂/Cards类上的一个方法。理想情况下,该方法将是一个初始化程序,而不是一个普通的设置程序,因为在创建 Cards 实例后卡片不应该直接可变
  • 所以,为了弄清楚这一点,你是说我唯一的选择是在我启动卡片实例时实际加载视图控制器中的图像吗?这不是违背模型的目的吗?对不起,如果我出去了,我正在努力解决这个问题。交易
  • 这取决于您对更改的兴趣程度。主要关键是控制器知道什么时候应该发生事情,但它不应该确切地知道发生了什么——它使用一些类型信息触发创建,但不知道类型信息对Cards 类的真正含义。工厂或Cards 类了解类型信息并获取相关图像
  • 好的,明白了。但是我如何实际将图像分配给数组。我的意思是基本上如何做到这一点。我创建了一个流行的 NSMutableArray *fruits; (或一个流行的 NSMutableArray *clothes; 或一个流行的 NSMutableArray *toys; 等等...)那么我实际上在哪里写行: self.fruits = [[NSMutableArray alloc] initWithObjects: [UIImage imageNamed:"CalliPad.png"] , [UIImage imageNamed:"CarryiPad.png"] 等...
猜你喜欢
  • 2011-07-11
  • 2015-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多