繁体   English   中英

Objective-C类集群层次结构的组织

[英]Objective-C organization of hierarchies of class clusters

这是Objective-C的类设计问题。 这是一个例子:

文件系统有文件和目录。 两者都是“节点”。 例如,走一个目录会产生一个节点列表,一些是[sub]目录,另一些是文件。

这指向类层次结构的以下客户端抽象视图:

@interface Node: NSObject {}
@end

@interface Directory: Node {}
@end

@interface File: Node {}
@end

到现在为止还挺好。 在这一点上,所有三个类都是抽象的。 现在开始实现,您会发现有两条主要路径:使用URL(Apple推荐用于Mac OSX≥10.6 )或路径(仅适用于Mac OSX≤10.5Cocotron )。

所以现在,您需要为上面三个抽象类中的每一个开发两个具体实现:

// Node subclasses
@class NodeWithPath;
@class NodeWithURL;

// Directory subclasses
@class DirectoryWithPath;
@class DirectoryWithURL;

// File subclasses
@class FileWithPath;
@class FileWithURL;

现在考虑一下FileWithURL

  • 它是一个文件,所以它应该从File继承。
  • 它是一个用URL实现的节点,因此它应该从NodeWithURL继承

FileNodeWithURL不在同一个类层次结构行中。 没有多重继承,就无法在Objective-C中表达这一点。

那你怎么设计这种情况呢? 我可以看到两个想法:

  • 使用协议,这是多重继承的有限形式。
  • 使用成员(has-a而不是is-a关系)。

我倾向于赞成协议的想法。 在这种情况下, DirectoryFile将是协议,并且六个具体类将从公共Node超类继承并符合其对应协议。 Node有两个子类层次结构:一个使用URL,一个使用Paths。

现在存在从客户端代码隐藏实现的问题。 可以使用Node公共超类为此目的设置类集群。 根据具体情况,客户端代码将获得类型为Node<File>Node<Directory>的对象。

任何其他/其他/类似/不同的想法?

也许我错过了一个明显的问题,但是......为什么你需要一个URL和一个对象的路径实现? 您似乎可以将路径存储为URL,并根据需要在两者之间进行转换。 您的课程的合理实施可能是:

@interface FileSystemNode : NSObject
{
    NSURL *URL;
}
@property (retain) NSURL *URL;
@property (retain) NSString *path;
- (id)initWithURL:(NSURL *)aURL;
- (id)initWithPath:(NSString *)aPath;
@end

@implementation FileSystemNode

@synthesize URL;

- (id)initWithURL:(NSURL *)aURL
{
    if ((self = [super init])) {
        [self setURL:aURL];
    }
    return self;
}

- (id)initWithPath:(NSString *)aPath
{
    return [self initWithURL:[NSURL fileURLWithPath:[aPath stringByExpandingTildeInPath]]];
}

- (void)dealloc
{
    [URL release];
    [super dealloc];
}

- (NSString *)path
{
    return [[self URL] path];
}

- (NSString *)setPath:(NSString *)path
{
    [self setURL:[NSURL fileURLWithPath:[path stringByExpandingTildeInPath]]];
}

@end

@interface File : FileSystemNode
@end

@interface Directory : FileSystemNode
@end

更新(根据评论)

在更一般的情况下,为顶层“对象”使用协议可能更容易,然后让每个具体实现实现协议。 您还可以使用类集群来使公共接口更干净,因此您只需拥有FileDirectory类,而不是每种类型的后备存储类。 这也允许您在删除对旧版本框架的支持时轻松交换实现。 像这样的东西:

#import <Foundation/Foundation.h>

// FileSystemNode.h
@protocol FileSystemNode
@property (readonly) NSURL *URL;
@property (readonly) NSString *path;
@end

// File.h
@interface File : NSObject <FileSystemNode>
- (id)initWithURL:(NSURL *)aURL;
- (id)initWithPath:(NSString *)aPath;
@end

// File.m

@interface URLFile : File
{
    NSURL *URL;
}
- (id)initWithURL:(NSURL *)aURL;
@end

@interface PathFile : File
{
    NSString *path;
}
- (id)initWithPath:(NSString *)aPath;
@end

@implementation File

- (id)initWithURL:(NSURL *)aURL
{
    [self release];
    return [[URLFile alloc] initWithURL:aURL];
}

- (id)initWithPath:(NSString *)aPath
{
    [self release];
    return [[PathFile alloc] initWithPath:aPath];
}

- (NSURL *)URL
{
    [self doesNotRecognizeSelector:_cmd];
}

- (NSString *)path
{
    [self doesNotRecognizeSelector:_cmd];
}

@end

@implementation URLFile

- (id)initWithURL:(NSURL *)aURL
{
    if ((self = [super init])) {
        URL = [aURL retain];
    }
    return self;
}

- (NSURL *)URL
{
    return [[URL retain] autorelease];
}

- (NSString *)path
{
    return [URL path];
}

@end

@implementation PathFile

- (id)initWithPath:(NSString *)aPath
{
    if ((self = [super init])) {
        path = [aPath copy];
    }
    return self;
}

- (NSURL *)URL
{
    return [NSURL fileURLWithPath:path];
}

- (NSString *)path
{
    return [[path retain] autorelease];
}

@end

我省略了Directory的实现,但它会是类似的。

我想你甚至可以走得更远。 在Unix上,目录一个具有一些特殊属性的文件,所以也许Directory甚至可以从File继承(虽然这对类集群来说有点难看,所以如果这样做会谨慎行事)。

如果您需要支持缺少NSURL采用所需方法版本的系统,只需使用路径即可。 然后,当您放弃对这些系统的支持时,转换过来,这将需要20分钟。 如果您必须使用这个超级复杂的系统来管理它们,那么使用URL几乎肯定不值得。

我不相信在这种情况下有一个很好的理由,在这种情况下只需要注意数据的来源(URL或路径 - 并且路径可以表示为文件:// URL)。

我的感觉是,另一种模式更适合这种情况。 我认为这是装饰器模式 - 只为每个文件配备一个“source”属性,在这个例子中可以是URL或文件相关的。 在处理对象时它非常方便,因为有关此属性的整个逻辑可以放入这些辅助对象中。 以后也很容易延长。

在一般情况下,我认为协议是要走的路,但你应该总是问自己是否真的需要表达差异(这里是URL与文件)。 通常,该代码的用户(甚至是库本身)根本不应该关心。

您可以完全将路径/ url属性与节点分离; 它们更像是节点层次结构的隐式属性而不是其中一个节点本身,并且您可以轻松地从节点计算一个或另一个,前提是它们都有父节点。

如果使用不同的工厂来创建路径/ URL,则可以交换或扩展命名系统,而无需触及节点层次结构类。

继续沿着这条路径,如果将所有文件操作移动到单独的类中,您将在Node层次结构中没有依赖于版本的代码。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM