1

我编写 C 代码已经有一段时间了,但对 Objective-C 和 OO 程序设计还是很陌生。我在如何设计我的程序和它的类上遇到了麻烦——我的备用过程编程范式将一个不透明的指针传递给所有东西,只是不会安静地死去。我正在用 Objective-C 的 Xcode4 编写一个简单的程序,我对设计和约定有一些疑问。我设计了两个类“PacketCapture”和“IPGeo”。第一个是 libpcap 的轻量级包装器,添加了一些解析逻辑,第二个是 MaxMind 的 GeoIP C API 的轻量级包装器。在我的主模块中,我实例化了 PacketCapture 类并将控制权传递给数据包捕获循环:

int main(int argc, const char *argv[])
{
    @autoreleasepool
    {
        PacketCapture *_PacketCapture = [[PacketCapture alloc] init:"en1" withFilter:""];
        if (_PacketCapture == nil)
        {
            NSLog(@"Can't instantiate PacketCapture class");
            return (-1);
        }
        IPGeo *_IPGeo = [[IPGeo alloc] init:"/usr/local/share/GeoIP/GeoIPCity.dat"];
        if (_IPGeo == nil)
        {
            NSLog(@"Can't instantiate IPGeo class");
            return (-1);
        }

        NSLog(@"entering packet loop...");
        [_PacketCapture packetCapture];
    }
    return (0);
}

在该packetCapture方法内部调用了一个解析函数,该函数将数据包切碎并显示出来。我需要在解析代码中使用 GeoIP 逻辑来针对地理数据库查找 IP 地址。这就是我的问题和普遍的困惑。

我的问题:

  • 我应该在哪里实例化 GeoIP 类?

  • 在类糟糕的 OO 设计中,对象内部是否存在无限循环?感觉还可以。也许如果我重组这个问题会很容易解决?

谢谢你的帮助。

4

3 回答 3

1

这段代码有几个问题:

  1. 您正在使用保留标识符(任何以两个下划线或一个下划线后跟一个大写字母开头的标识符都是保留的)。正如我在评论中提到的,它不太可能引起任何实际问题,但这是需要考虑的问题。

  2. 对于初始化方法,一种更“可可”的方法是接受文件名或NSData包含文件内容的对象。后者的好处是数据不一定必须来自磁盘上的文件。例如:

    NSData *geoData = [NSData dataWithContentsOfFile:@"/path/to/file"];
    IPGeo *ipGeo = [[IPGeo alloc] initWithData:geoData];
    

    在该initWithData:方法中,您可以将数据解析为原始字节,或者将其转换为字符串并解析,等等。无论如何,init:这对于方法来说不是一个很好的名称,因为不清楚该方法期望什么。如果您仍然希望它接受文件名,则可能是一个好名称initWithContentsOfFile:(并让它接受 anNSString而不仅仅是 a const char *)。

    同样,使用您的PacketCapture初始化方法。Objective-C 为您提供了能够命名参数的好处,这允许半自文档化代码,因此方法命名在 Objective-C 中非常重要,即使只是为了与所有其他常用框架保持一致。

  3. 在方法中实现无限循环并没有错,这或多或少是NSApplication对其run方法的作用。对此有替代方案,例如线程化或使用运行循环等,但对于小型命令行实用程序而言,很少有任何好处。我建议稍微扩展和重命名您的方法:

    // Methods are usually named as if you are telling the object what to do
    [packetCapture capturePackets];
    
    // or, if you think this might be of some benefit:
    NSDate *stopTime = /* specify a time to stop */;
    [packetCapture capturePacketsUntilDate:stopTime];
    
    // This could also allows for indefinite running with:
    [packetCapture capturePacketsUntilDate:[NSDate distantFuture]];
    
  4. 如果PacketCapture需要一个实例IPGeo来运行,那么PacketCapture应该接受IPGeo它作为其初始化程序的一部分(再次,我将方法重命名为我认为应该是的):

    PacketCapture *packetCapture = [[PacketCapture alloc] initWithIPGeo:ipGeo interface:@"en1"];
    

    或者,PacketCapture该类可以分配和初始化IPGeo该类本身,但这会在两个类之间创建紧密的耦合。

于 2012-05-14T23:39:37.190 回答
0

你说的“GeoIP类”是什么意思IPGeo?在哪里实例化它取决于几个因素。如果您只需要在IPGeo类中的一个解析方法中使用它,请在此处实例化它。如果您在对象的整个生命周期中都需要它,请IPGeo在指定的初始化程序中对其进行实例化,并将其保存在实例变量中。

无限循环本质上并不是一件坏事,但通常有更好的方法。你在开发什么平台?

顺便说一句,NSString文字有一个@前缀,例如:@"foo"。您正在使用纯 C 字符串,如果您将它们传递到任何期望NSString.

于 2012-05-14T23:01:53.887 回答
0

我应该在哪里实例化 GeoIP 类?

如果PacketCapture需要一个IPGeo对象来运行,我建议在PacketCapture's-init方法中实例化它。如果您需要在使用IPGeo对象之前配置对象(例如,通过在初始化时传递不同的路径)PacketCapture,并且使用的路径以PacketCapture不应该知道的方式确定,则让类负责管理这两者(或者,在您的情况下,程序的主循环)创建一个实例并将其作为参数传递给PacketCapture该类的自定义初始化方法。

有点吹毛求疵——你的初始化IPGeo器不遵循表达方法名称的 Objective-C 约定。-initWithPathToDatabase:在我看来会看起来好多了。

类似地,您可以向您的类添加一个-initWithIPGeo:方法。PacketCapture

在类糟糕的 OO 设计中,对象内部是否存在无限循环?

如果这是一个 iOS 或 Cocoa 应用程序,那么肯定是的。如果它是一个命令行工具,那么也许。此外,如果该无限循环正在主线程上运行,它将阻塞所有 UI 元素,这将使您的程序看起来没有响应。

更好的选择是使用协议/委托模式,这是 Objective-C 程序的主要部分。使用以下方法创建协议:

- (void)packetCapture:(PacketCapture *)packetCapture didReceiveDataFromIPAddress:(NSString *)ipAddress;

然后创建一个实现此协议并在接收数据包时执行操作的委托对象。如果您正在制作命令行工具,则可以实例化该委托并等待接收消息。

于 2012-05-14T23:08:20.667 回答