৬ – অবজেক্টিভ সি (Objective C) তে মেথড (method) এর বিস্তারিত

Standard

আগের চ্যাপ্টারঃ ৫ – অবজেকটিভ সি (OBJECTIVE C) তে প্রোপার্টি ও এর অ্যাট্রিবিউটের ব্যবহার

ভূমিকাঃ
মেথড যেকোনো কিছুর কার্যপ্রণালি বর্ণনা করে এবং সেটা কখন ঘটাতে হবে তা নির্ভর করে একটি অবজেক্ট এর উপর যার মাধ্যমে ওই মেথডকে প্রয়োজন অনুযায়ী কল করা হয়। এই চ্যাপ্টারে আমরা অবজেক্টিভ সি তে মেথডের নেমিং কনভেনশন, ব্যবহার এবং এক্সেস মডিফায়ার নিয়ে আলোচনা করব। আরও থাকবে, কিভাবে সিলেক্টর ব্যবহার করে মেথডের রেফারেন্স করা যায় এবং ডাইনামিক্যালি মেথড কল করা যায়।

নেমিং কনভেনশনঃ
সাধারণত অবজেক্টিভ সি তে মেথডের নামকরন করার সময় বেশি বেশি শব্দ ব্যবহার করা হয় বলে আপাত দৃষ্টিতে মনে হয় কিন্তু সত্যি বলতে এই পদ্ধতিতে মেথডটি অনেক বেশি বর্ণনামূলক ও বোধগম্য হয়। কোন রকম শব্দের সংক্ষেপণ না করা এবং বিশদভাবে মেথডটির প্যারামিটার ও রিটার্ন ভ্যালু ঠিক করে দেয়াই হচ্ছে অবজেক্টিভ সি তে মেথডের নামকরণের অঘোষিত নিয়ম।

নিচে কিছু উদাহরণ দেয়া হল,

// Calculated values
- (double)maximumPreparationTime;
- (double)maximumPreparationTimeUsingTool:(NSString *)tool;

// Action methods
- (void)startCooking;
- (void)cookFixedSize:(double)theSize;
- (void)makeWithType:(NSString *) type withSize:(int) size;

// Custom Constructor methods
- (id)initWithItem:(NSString *)item;
- (id)initWithItem:(NSString *)item size:(double)theSize;

// Factory methods
+ (Food *)food;
+ (Food *)foodWithItem:(NSString *)item;

মেথডের নাম ও কাজকে সহজ বোধগম্য করার সবচেয়ে সাধারণ উপায় হচ্ছে এর নামের শব্দ গুলোকে সংক্ষেপ না করা। যেমন উপরে একটি মেথডের নাম লেখা হয়েছে maximumPreparationTime যেটাকে হয়তবা এই নিয়মের বাইরে গিয়ে maxPrepTime ও লেখা যেত।

C++ বা Java তে যেখানে মেথড এবং এর প্যারামিটার গুলোকে আলাদা ভাবেই ট্রিট করা হয়, সেখানে অবজেক্টিভ সি তে একটি মেথডের নামের সাথে এর প্যারামিটার গুলোর নামও যুক্ত থাকে। যেমন উপরের initWithItem:size: মেথডটি দুইটি প্যারামিটার গ্রহণ করে আর তাই এই মেথডের নাম মূলত initWithItem:size: এটাই। শুধুমাত্র initWithItem: নয়। এখানে initWithItem:size: নামটি প্রথম আর্গুমেন্ট হিসেবে item এবং দ্বিতীয় আর্গুমেন্ট হিসেবে size প্রকাশ করছে।

আরও একটি উল্লেখযোগ্য বিষয় হচ্ছে মেথড গুলোর রিটার্ন করা ভ্যালু আসলেই কি জিনিস সেটাও মেথড ডিক্লেয়ার করার সময়ই পরিষ্কার ভাবে ঠিক করে দেয়া হয়। যেমন উপরের ফ্যাক্টরি মেথড গুলো দেখে বোঝা যাচ্ছে যে, এগুলো Food ক্লাসের ইন্সট্যান্স রিটার্ন করবে।

মেথড কল করাঃ
একটি স্কয়ার ব্রাকেটের মধ্যে প্রথমে অবজেক্টের নাম এবং একটা স্পেস দিয়ে মেথডের নাম লিখে যেকোনো অবজেক্টের মেথডকে কল করা হয়। প্যারামিটার ওয়ালা মেথডের ক্ষেত্রে একটি কোলন দিয়ে মেথডের নাম এবং আর্গুমেন্টকে আলাদা করা হয়। যেমন ৪ নাম্বার চ্যাপ্টারের অনেক জায়গাতেই মেথড কল দেখানো হয়েছে। নিচে আবার আমরা এক সেট উদাহরণ তৈরি করব এই চ্যাপ্টারের টপিক গুলো বোঝানোর জন্য,

// Food.h

#import <Foundation/Foundation.h>

@interface Food : NSObject

@property (nonatomic) int price;

- (void)makeWithType:(NSString *) type withSize:(int) size;

@end
// Food.m

#import "Food.h"

@implementation Food

- (void)makeWithType:(NSString *)type withSize:(int)size {
    
    // Setting price property
    self.price = size*60;
    
    // Logging in the console
    NSLog(@"Making a %d inch %@ that costs %d Tk", size, type, self.price);
}

@end
// main.m

#import <Foundation/Foundation.h>
#import "Food.h"

int main(int argc, const char * argv[])
{
    
    @autoreleasepool {

        Food *pizza = [[Food alloc] init];

        [pizza makeWithType:@"Mexican Pizza" withSize:6];
        
    }
    return 0;
}

উপরে খুব সহজ একটি উদাহরণ দেখানো হয়েছে যেখানে প্রথমে Food ক্লাসের ইন্টারফেসে একটি প্রোপার্টি price এবং একটি মেথড makeWithType:withSize: ডিক্লেয়ার করা হয়েছে এবং এদের ইমপ্লেমেন্টেশনটাও খুব সহজ যেখানে এই মেথড তার দ্বিতীয় আর্গুমেন্ট হিসেবে পাওয়া পিৎজা সাইজের সাথে একটা ভ্যালু ৬০ গুন করে price প্রোপার্টির ভ্যালু সেট করছে এবং শেষে কনসোলে একটি ম্যাসেজ প্রিন্ট করছে।
ইতোমধ্যে হয়ত খেয়াল করেছেন একাধিক প্যারামিটার ওয়ালা মেথডকে নিচের মত করে কল করতে হয়,

[pizza makeWithType:@"Mexican Pizza" withSize:6];

অর্থাৎ- দ্বিতীয় এবং এর পরবর্তী প্যারামিটার গুলো, ইনিশিয়ালটির (প্রথম) পর পরই থাকে এবং প্রত্যেকটি প্যারামিটার তার আগেরটির সাথে একটি “স্পেস লেবেল:ভ্যালু” প্যাটার্ন মেইন্টেইন করে।

এই অদ্ভুত নিয়মটাই কিন্তু এদানিং বেশি হিউম্যান রিডেবল মনে করা হয়। অনেকদিন পর যদি হুট করে কোনদিন আপডেট অথবা বাগ ফিক্সিং এর উদ্দেশ্যে আগের করা প্রজেক্টটি আপনি যদি নতুন করে বুঝতে চান তখনই আসলে বোঝা যাবে এভাবে নেমিং কনভেনশন মেনে মেথডের নামকরনের সুবিধা।

নেস্টেড মেথড কলঃ
নেস্টেড মেথড কলিং, অবজেক্টিভ সি এর একটি কমন প্যাটার্ন। একটি মেথডের রিটার্ন করা ভ্যালু অন্য মেথডে পাঠানোর স্বাভাবিক উপায়কেই নেস্টেড মেথড কল বলা হয়। অন্যান্য ল্যাঙ্গুয়েজের ভিউ থেকে থেকে দেখলে এটা একধরনের চেইনিং মেথড কল। উপরের উদাহরণেও কিন্তু একটা নেস্টেড মেথড কল হয়েছে। main.m ফাইলের ১২ নাম্বার লাইনে,

Food *pizza = [[Food alloc] init];

এখানে [Food alloc] এর মাধ্যমে একবার alloc মেথড কল করে অবজেক্টের জন্য মেমোরি অ্যালোকেট করা হয়েছে এবং তারপরই এটার রিটার্ন ভ্যালুকে দিয়ে init মেথড কল করা হয়েছে।

প্রোটেক্টেড এবং প্রাইভেট মেথডঃ
অবজেকটিভ সি তে কোন প্রোটেক্টেড অথবা প্রাইভেট এক্সেস মডিফায়ার নেই। এতে সকল মেথডই পাবলিক যদি সেগুলো ইন্টারফেস/এপিআই/পাবলিক লেয়ারে ডিক্লেয়ার করা হয়। তারপরও কোন মেথড এর ডিক্লেয়ারেশন হেডার ফাইলে না লিখে যদি শুধু ইমপ্লিমেনটেশন ফাইলে লেখা হয় তাহলে মেথডটি প্রাইভেট মেথডের মত আচরন করে। কারন, অন্যান্য অবজেক্ট বা সাবক্লাস সাধারণত .m বা ইমপ্লেমেন্টেশন ফাইলকে ইম্পোর্ট করে না আর তাই ওগুলো শুধুমাত্র ওই ক্লাসেরই অ্যাসেট হিসেবে থেকে যায়। একি কথা প্রোপার্টির জন্যও প্রযোজ্য।

নিচের ৩টি ফাইল মিলে এরকম একটি কেস তৈরি করা হয়েছে।

// Food.h

#import <Foundation/Foundation.h>

@interface Food : NSObject

-(void)makeWithType:(NSString *) type withSize:(int) size;

@end
// Food.m

#import "Food.h"

@implementation Food {
    // The following variable is not directly accessible by any object of Food class
    int _price;
    
}

// The following method is not directly accessible by any object of Food class
- (int) calculatePrice: (int) size {
    
    // Setting the value of a private instance variable
    _price = size*60;
    
    return _price;
    
}

- (void)makeWithType:(NSString *)type withSize:(int)size {

    // Calling a kind of private method
    [self calculatePrice:size];
    
    // Logging in the console
    NSLog(@"Making a %d inch %@ that costs %d Tk", size, type, _price);
}

@end
// main.m

#import <Foundation/Foundation.h>
#import "Food.h"

int main(int argc, const char * argv[])
{
    
    @autoreleasepool {

        Food *pizza = [[Food alloc] init];
        
        // [pizza calculatePrice:6]; // Is not accessible like this way 
        
        [pizza makeWithType:@"Mexican Pizza" withSize:6];
        
    }
    return 0;
}

এখানে _price ভ্যারিয়েবলটি এবং calculatePrice: মেথডটি প্রাইভেট এর মত আচরন করে। অর্থাৎ উপরের উদাহরণটি ঠিক ঠাক মতোই কাজ করবে কিন্তু যদি main.m ফাইলের ১৩ নাম্বার লাইনটি আনকমেন্ট করে প্রোগ্রামটি রান করতে চান তাহলে Xcode এরর ম্যাসেজ দিয়ে জানাবে যে Food ইন্টারফেসে calculatePrice: নামে কোন মেথড নাই।
Screenshot 2014-05-24 19.12.27
যার মানে দাড়ায়, এই মেথডকে আপনি ওই ক্লাসের বাইরে থেকে ব্যবহার করতে পারছে না। কিন্তু calculatePrice: মেথডটি ঠিকই আমরা ব্যবহার করতে পারছি Food.m ফাইলে অর্থাৎ ক্লাসের ভিতরে, যেমন ২৪ নাম্বার লাইনে।

অন্যদিকে অবজেক্টিভ সি তে, প্রোটেক্টেড মেথডের বিকল্প এবং এর বিস্তারিত ব্যবহার পাওয়া যাবে ক্যাটাগরি সেকশনে। অবজেক্টিভ সি তে ক্যাটাগরি নিয়ে সামনেই পুরো একটা চ্যাপ্টার আসছে। তাই এখানে আর এটা নিয়ে আলোচনা করা হচ্ছে না।

সিলেক্টরঃ
অবজেক্টিভ সি তে সিলেক্টর হচ্ছে কোন মেথডের নামের ইন্টারনাল রিপ্রেজেন্টেশন। এর মাধ্যমে আপনি একটি মেথডকে একটি স্বাধীন এলিমেন্ট হিসেবে এর সাথে ডিল করতে পারবেন। যেমন, একটি অবজেক্ট সাধারণত একটি অ্যাকশনকে ব্যবহার করে আর এ ক্ষেত্রে সিলেক্টর অবজেক্ট থেকে ওই অ্যাকশনকে আলাদা করে। এটাকে Target-Action ডিজাইন প্যাটার্ন এর ভিত্তি বলা যায়। আর সহজ ভাবে বলতে গেলে কোন মেথডকে ডাইন্যামিক্যালি ইনভোক/কল করার জন্য সিলেক্টরের দরকার পরে।

দুইটি পদ্ধতিতে একটি মেথড এর নামের সাপেক্ষে একটি সিলেক্টর পাওয়া যায়। @selector() ডিরেক্টিভ ব্যবহার করে অথবা NSSelectorFromString() ফাংশনের সাহায্যে। দুইটি ক্ষেত্রেই এরা আপনার নতুন সিলেক্টর এর জন্য একটি ডাটা টাইপ রিটার্ন করে যার নাম SEL. অন্যান্য সাধারণ ডাটা টাইপ যেমন BOOL, int এর মতই এই SEL কেও একটি ডাটা টাইপ মনে করা যেতে পারে।

নিচের মত করে Food ক্লাসের ইন্টারফেস, ইমপ্লেমেন্টেশন এবং main ফাইলের কোড আপডেট করুন। তারপর আমরা সিলেক্টর এর ব্যবহার আরও ভাল ভাবে উপলব্ধি করতে পারবো।

// Food.h

#import <Foundation/Foundation.h>

@interface Food : NSObject

- (void)placeOrder;
- (void)makeOfType:(NSString *) type ofSize:(NSNumber *) size;

@end
// Food.m

#import "Food.h"

@implementation Food

- (void)placeOrder {
    NSLog(@"Order Received!");
}

- (void)makeOfType:(NSString *)type ofSize:(NSNumber *)size {
    
    NSLog(@"Making a %@ inch %@", size, type);
}

@end
// main.m

#import <Foundation/Foundation.h>
#import "Food.h"

int main(int argc, const char * argv[])
{
    
    @autoreleasepool {

        Food *choice = [[Food alloc] init];
        
        SEL stepOne = NSSelectorFromString(@"placeOrder");
        SEL stepTwo = @selector(makeOfType:ofSize:);

        #pragma clang diagnostic push
        #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        
        // Same as calling this way: [choice placeOrder];
        [choice performSelector:stepOne];
        
        // Same as calling this way:
        // [choice makeOfType:@"Mexican Pizza" ofSize:[NSNumber numberWithInt:6]];
        [choice performSelector:stepTwo
                     withObject:@"Mexican Pizza"
                     withObject:[NSNumber numberWithInt:6]];
        
        #pragma clang diagnostic pop
    }
    return 0;
}

এখানে খুব স্বাভাবিক ভাবেই ইন্টারফেস এবং ইমপ্লেমেন্টেশন ঠিক করা হয়েছে যে ব্যপারে আপনি ইতোমধ্যে কয়েকবার ধারনা পেয়েছেন। এরপর main.m ফাইলে আমরা দুটি সিলেক্টর তৈরি করে নিয়েছি উপরে উল্লেখিত দুটি পদ্ধতি ব্যবহার করেই ১৩ এবং ১৪ নাম্বার লাইনে। এর পর ১৬, ১৭ এবং ২৮ নাম্বার লাইনের কোড গুলো আমাদের সিলেক্টর এর ব্যবহার সম্পর্কিত নয় বরং কম্পাইলারের কিছু ওয়ার্নিং বন্ধ করার জন্য। এভাবে সিলেক্টর ডিফাইন করলে কম্পাইলার বুঝে উঠতে পারে না যে, এই মেথড গুলোর রেজাল্ট দিয়ে কি করা হবে। তাই Xcode থেকে নিচের মত ওয়ার্নিং পেতে পারেন,
Screen Shot 2014-05-25 at 2.00.16 AM
এই ওয়ার্নিং এর ব্যপারে আরও বিস্তারিত এখানে

এরপর ২০ এবং ২৪ নাম্বার লাইনে আমরা performSelector: মেথড ব্যবহার করে আমাদের তৈরি করা সিলেক্টর গুলোকে এক্সিকিউট করেছি। ১৯ এবং ২২ নাম্বার লাইনে কমেন্ট এর মধ্যে বলা হয়েছে এর সমতুল্য ট্র্যাডিশনাল কল কেমন হতে পারতো। একাধিক প্যারামিটার ওয়ালা মেথডের ক্ষেত্রে withObject: ব্যবহার করে একের পর এক প্যরামিটার পাস করা হয়।

প্রোগ্রামটি রান করালে নিচের মত আউটপুট আসবে,
Screen Shot 2014-05-25 at 2.07.25 AM

পরের চ্যাপ্টারঃ
এই চ্যাপ্টার পর্যন্ত অবজেক্টিভ সি এর ব্যাসিক ব্যপার গুলো সংক্ষেপে তুলে ধরা হয়েছে। পরবর্তী চ্যাপ্টার থেকে একটু অ্যাডভ্যান্স বিষয় নিয়ে আলোচনা হবে। যেমন ৭ নাম্বার চ্যাপ্টারেই থাকবে প্রোটোকল (protocol) যার মাধ্যমে একটি এপিআই কে বিভিন্ন রকম এবং অনেক
গুলো ক্লাস থেকে ব্যবহার করা যাবে।

পরের চ্যাপ্টারঃ অবজেক্টিভ সি (OBJECTIVE C) এর প্রোটোকল ও এর ব্যবহার

Advertisements

3 thoughts on “৬ – অবজেক্টিভ সি (Objective C) তে মেথড (method) এর বিস্তারিত

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s