سؤال

في روبي، هناك وحدات ويمكنك توسيع الفصل الدراسي عن طريق "خلط" الوحدة.

module MyModule
  def printone
    print "one" 
  end
end

class MyClass
  include MyModule
end

theOne = MyClass.new
theOne.printone 
>> one

في Objective-C، أجد أن لدي مجموعة من الأساليب الشائعة التي أريد أن يرثها عدد من الفئات.ما هي الطرق الأخرى التي يمكنني من خلالها تحقيق ذلك دون إنشاء فئة مشتركة واستخلاص كل شيء من تلك الطبقة المشتركة؟

هل كانت مفيدة؟

المحلول

يحرر:تمت إضافة التغييرات لأن بعض الأشخاص يشعرون أنني مسؤول عن القيود المفروضة على Objective-C.

اجابة قصيرة:لا يمكنك.لا يحتوي Objective-C على ما يعادل مزيج روبي.

إجابة قصيرة أقل قليلا:يحتوي Objective-C على شيء له نفس النكهة:البروتوكولات.البروتوكولات (الواجهات في بعض اللغات الأخرى)، هي طريقة لتحديد مجموعة من الأساليب التي تتبنى فئة ما والتي تلتزم البروتوكولات بتنفيذها.لا يوفر البروتوكول التنفيذ بالرغم من ذلك.يمنع هذا القيد استخدام البروتوكولات كمعادل دقيق لخلطات روبي.

إجابة أقل اختصارًا: ومع ذلك، يحتوي وقت تشغيل Objective-C على واجهة برمجة تطبيقات مكشوفة تتيح لك اللعب بالميزات الديناميكية للغة.ثم تخرج عن اللغة، ولكن يمكن أن يكون لديك بروتوكولات ذات تطبيقات افتراضية (وتسمى أيضًا البروتوكولات الملموسة).تظهر إجابة فلاديمير طريقة واحدة للقيام بذلك.عند هذه النقطة يبدو لي أنك حصلت على مزيج روبي على ما يرام.

ومع ذلك، لست متأكدًا من أنني سأوصي بذلك.في معظم الحالات، هناك أنماط أخرى تناسب الفاتورة دون ممارسة الألعاب مع وقت التشغيل.على سبيل المثال، يمكن أن يكون لديك كائن فرعي يقوم بتنفيذ الطريقة المختلطة (لديه أ بدلاً من هو).اللعب في وقت التشغيل أمر جيد، ولكن له عيبان:

  • أنت تجعل التعليمات البرمجية الخاصة بك أقل قابلية للقراءة لأنها تتطلب من القراء معرفة أكثر بكثير من اللغة.بالتأكيد يمكنك (ويجب عليك) التعليق عليه، لكن تذكر أن أي تعليق ضروري يمكن اعتباره عيبًا في التنفيذ.

  • أنت تعتمد على الذي - التي تنفيذ اللغة.من المؤكد أن منصات Apple هي الأكثر شيوعًا لـ Objective-C ولكن لا تنس Cocotron أو GnuStep (أو Etoilé) التي لها أوقات تشغيل مختلفة، والتي قد تكون أو لا تكون متوافقة مع Apple في هذا الصدد.

كملاحظة جانبية، أذكر أدناه أن الفئات لا يمكنها إضافة حالة (متغيرات الحالة) إلى الفصل.وباستخدام واجهة برمجة التطبيقات لوقت التشغيل، يمكنك رفع هذا القيد أيضًا.لكن هذا خارج نطاق هذه الإجابة.

اجابة طويلة:

تبدو ميزتان من ميزات Objective-C كمرشحين محتملين:الفئات والبروتوكولات.الفئات ليست حقًا الاختيار الصحيح هنا، إذا فهمت السؤال بشكل صحيح.الميزة الصحيحة هي البروتوكول.

اسمحوا لي أن أقدم مثالا على ذلك.لنفترض أنك تريد أن تتمتع مجموعة من فصولك بقدرة محددة تسمى "الغناء".ثم تقوم بتحديد البروتوكول:

@protocol Singer
    - (void) sing;
@end

يمكنك الآن أن تعلن أن أيًا من فصولك الدراسية تعتمد البروتوكول بالطريقة التالية:

@interface Rectangle : Shape <Singer> {
    <snip>
@end

@interface Car : Vehicle <Singer> {
    <snip>
@end

ومن خلال إعلان اعتمادهم للبروتوكول، فإنهم يلتزمون بتنفيذه sing طريقة.على سبيل المثال:

@implementation Rectangle

- (void) sing {
    [self flashInBrightColors];
}

@end

@implementation Car

- (void) sing {
    [self honk];
}

@end

ثم تستخدم هذه الفئات على سبيل المثال مثل هذا:

void choral(NSArray *choir) // the choir holds any kind of singer
{
    id<Singer> aSinger;
    for (aSinger in choir) {
        [aSinger sing];
    }
}

لاحظ أن المطربين في المصفوفة لا يحتاجون إلى فئة فائقة مشتركة.لاحظ أيضًا أن الفصل يمكن أن يحتوي على فئة فائقة واحدة فقط، ولكن هناك العديد من البروتوكولات المعتمدة.لاحظ أخيرًا أن التحقق من النوع يتم بواسطة المترجم.

في الواقع، آلية البروتوكول عبارة عن وراثة متعددة تستخدم لنمط الخلط.هذا الميراث المتعدد محدود للغاية لأن البروتوكول لا يمكنه إضافة متغيرات مثيل جديدة إلى الفصل.يصف البروتوكول فقط الواجهة العامة التي يجب على المتبنين تنفيذها.على عكس وحدات روبي، فهي لا تحتوي على تطبيق.

هذا هو أكثر من ذلك.دعونا نذكر الفئات ولكن.

يتم الإعلان عن الفئة ليس بين قوسين، ولكن بين قوسين.الفرق هو أنه يمكن تعريف فئة لفئة موجودة لتوسيعها دون تصنيفها إلى فئات فرعية.يمكنك حتى القيام بذلك لفئة النظام.كما يمكنك أن تتخيل، من الممكن استخدام الفئات لتنفيذ شيء مشابه لـ mixin.وقد تم استخدامها بهذه الطريقة لفترة طويلة عادةً كفئة NSObject (الجذر النموذجي للتسلسل الهرمي للميراث)، لدرجة أنها كانت تسمى البروتوكولات "غير الرسمية".

إنه غير رسمي لأنه 1- لا يتم التحقق من النوع بواسطة المترجم، و 2- يعد تنفيذ أساليب البروتوكول أمرًا اختياريًا.

ليست هناك حاجة اليوم لاستخدام الفئات كبروتوكولات، خاصة وأن البروتوكولات الرسمية يمكنها الآن الإعلان عن أن بعض أساليبها اختيارية باستخدام الكلمة الأساسية @optional أو مطلوب (الافتراضي) مع @required.

لا تزال الفئات مفيدة لإضافة بعض السلوكيات الخاصة بالمجال إلى فصل دراسي موجود. NSString هو هدف مشترك لذلك.

ومن المثير للاهتمام أيضًا الإشارة إلى أن معظم (إن لم يكن كل) من NSObject تم الإعلان عن المرافق في الواقع في أ NSObject بروتوكول.هذا يعني أنه ليس مقنعًا حقًا للاستخدام NSObject باعتبارها طبقة عليا مشتركة لجميع الطبقات، على الرغم من أن هذا لا يزال شائعًا لأسباب تاريخية، وحسنًا...لأنه ليس هناك عيب في القيام بذلك.لكن بعض فئات النظام، مثل NSProxy, ، نكون لا NSObject.

نصائح أخرى

المكونات وقح: ObjectiveMixin

إنه يستفيد من قدرة وقت تشغيل Objective-C على إضافة طرق إلى فئة في وقت التشغيل (على عكس الفئات، التي تكون في وقت الترجمة فقط).التحقق من ذلك، فهو يعمل بشكل جيد جدًا وبطريقة مشابهة لخلطات روبي.

يمكنك خلط الكود حرفيًا باستخدام #include.هذا غير مستحسن وهو مخالف لجميع الأديان في الموضوع ج، ولكنه يعمل بشكل مثالي.

من فضلك، لا تفعل ذلك في رمز الإنتاج.

على سبيل المثال في الملف:

MixinModule.header (لا ينبغي تجميعها أو نسخها إلى الهدف)

-(void)hello;

MixinModule.body (لا ينبغي تجميعها أو نسخها إلى الهدف)

-(void)hello{
    NSLog(@"Hello");
}

في فئة ميكسين:

@interface MixinTest : NSObject
#include "MixinModule.header"
@end

@implementation MixinTest
#include "MixinModule.body"
@end

حالة الاستخدام:

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]){
    @autoreleasepool {
        [[[MixinTest new] autorelease] hello];
    }
    return 0;
}

من فضلك، لا تفعل ذلك في رمز الإنتاج.

هذا هو رأيي في تنفيذ Mixins في Objective-C، دون استخدام وقت تشغيل Objective-C مباشرة.ربما يكون مفيدًا لشخص ما: https://stackoverflow.com/a/19661059/171933

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top