Objective-C organization of class cluster hierarchies

This is a class design issue with Objective-C. Here is an example:

File systems have files and directories. Both are "nodes." Going through a directory, for example, gives a list of nodes, some of which are [sub] directories, others are files.

This indicates the following abstract view of the class hierarchy on the client side:

@interface Node: NSObject {}
@end

@interface Directory: Node {}
@end

@interface File: Node {}
@end

So far so good. At this point, all three classes are abstract. Now you go to the implementation, you understand that there are two main ways: using URLs (Apple recommended for Mac OS X ≥ 10.6) or paths (only for Mac OS X ≤ 10.5 or Cocotron ).

So, now you need to develop two specific implementations of each of the three abstract classes above:

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

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

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

Now consider, say FileWithURL:

  • , File.
  • node, URL-, NodeWithURL

File NodeWithURL . Objective-C.

, ? :

  • , .
  • (has-a is-a ).

. Directory File , Node . Node : URL-, .

. Node. , Node<File> Node<Directory>, .

/// ?

+3
4

, , ... 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

( )

"" , . , , File Directory, . , . - :

#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 ( , ).

+3

, NSURL-, . , , , 20 . URL- , .

+1

, , (URL - ://URL- ).

, . , - "source", URL . , . .

, - , , ( URL- ). ( ) .

0

You can completely separate the path / url property from the nodes; they are a more implicit property of the node hierarchy than one of the nodes themselves, and you can easily calculate one or the other of the node if they all have a parent element.

If you use different factories to create your path / URLs, you can change or expand your naming system without touching your node hierarchy classes.

Continuing this way, if you move all file operations to separate classes, you will not have version-specific code in the node hierarchy.

0
source

Source: https://habr.com/ru/post/1788161/


All Articles