সি ++ তে এই একটি জিনিস রয়েছে যা আমাকে দীর্ঘদিন ধরে অস্বস্তি বোধ করে চলেছে, কারণ এটি সহজ বলে মনে হলেও আমি কীভাবে এটি করতে তা সত্যতার সাথে জানি না:
আমি কীভাবে সি ++ এ কারখানার পদ্ধতি কার্যকর করব?
লক্ষ্য: অগ্রহণযোগ্য পরিণতি এবং পারফরম্যান্সের আঘাত ছাড়াই ক্লায়েন্টকে অবজেক্টের কনস্ট্রাক্টরের পরিবর্তে কারখানার পদ্ধতিগুলি ব্যবহার করে কিছু অবজেক্ট ইনস্ট্যান্ট করার অনুমতি দেওয়া সম্ভব করে তোলে।
"ফ্যাক্টরি মেথড প্যাটার্ন" দ্বারা, আমি উভয় স্থিতিশীল কারখানার পদ্ধতি কোনও অবজেক্টের ভিতরে বা অন্য শ্রেণিতে সংজ্ঞায়িত পদ্ধতিগুলি বা বৈশ্বিক ফাংশনগুলিকে বোঝাতে চাইছি। কেবলমাত্র "দশম শ্রেণির ইনস্ট্যান্টেশনের স্বাভাবিক পদ্ধতিটি কনস্ট্রাক্টর বাদে অন্য কোথাও পুনর্নির্দেশের ধারণা"।
আমি কিছু সম্ভাব্য উত্তর যা আমি ভেবে দেখেছি তা স্কিম করতে দিন।
0) কারখানা তৈরি করবেন না, নির্মাণকারী তৈরি করুন।
এটি দুর্দান্ত শোনাচ্ছে (এবং প্রায়শই সর্বোত্তম সমাধান) তবে এটি সাধারণ প্রতিকার নয়। প্রথমত, কিছু ক্ষেত্রে রয়েছে যখন অবজেক্ট নির্মাণ একটি টাস্ক জটিল হিসাবে এটি অন্যের ক্লাসে নিষ্কাশনকে ন্যায়সঙ্গত করতে পারে। এমনকি এমনকি এই বিষয়টিকে একপাশে রেখে দেওয়া, এমনকি কেবল সাধারণ নির্মাণকারী ব্যবহার করে সাধারণ বস্তুগুলির জন্যও প্রায়শই এটি করা হয় না।
আমি জানি সবচেয়ে সহজ উদাহরণটি একটি 2-ডি ভেক্টর শ্রেণি। এত সহজ, তবুও জটিল। আমি এটি উভয় কার্তেসিয়ান এবং মেরু স্থানাঙ্ক থেকে নির্মাণ করতে সক্ষম হতে চাই। স্পষ্টতই, আমি এটি করতে পারি না:
struct Vec2 {
Vec2(float x, float y);
Vec2(float angle, float magnitude); // not a valid overload!
// ...
};
আমার স্বাভাবিক চিন্তাভাবনাটি হ'ল:
struct Vec2 {
static Vec2 fromLinear(float x, float y);
static Vec2 fromPolar(float angle, float magnitude);
// ...
};
যা, নির্মাণকারীর পরিবর্তে, আমাকে স্থিতিশীল কারখানার পদ্ধতি ব্যবহারের দিকে পরিচালিত করে ... যার মূল অর্থ হ'ল আমি কারখানার প্যাটার্নটি বাস্তবায়ন করছি, কোনও উপায়ে ("শ্রেণিটি তার নিজস্ব কারখানায় পরিণত হয়")। এটি দেখতে দুর্দান্ত দেখাচ্ছে (এবং এই নির্দিষ্ট ক্ষেত্রে এটি উপযুক্ত হবে) তবে কিছু ক্ষেত্রে ব্যর্থ হয়, যা আমি ২ পয়েন্টে বর্ণনা করতে যাচ্ছি on
অন্য কেস: কিছু এপিআই-র দুটি অস্বচ্ছ টাইপডেফ (যেমন সম্পর্কিত সম্পর্কযুক্ত ডোমেনের জিইউইডি, বা একটি জিইউডি এবং বিটফিল্ড) দ্বারা শব্দ বোঝার সম্পূর্ণ ভিন্ন (যেমন - তত্ত্বের মধ্যে - বৈধ ওভারলোড) টাইপগুলি যা আসলে রূপান্তরিত হয় একই জিনিস - স্বাক্ষরবিহীন ints বা শূন্য পয়েন্টার মত।
1) জাভা ওয়ে
জাভাতে এটি সহজ, কারণ আমাদের কাছে কেবল গতিশীল-বরাদ্দকৃত বস্তু রয়েছে। কারখানা তৈরি করা ততটা তুচ্ছ
class FooFactory {
public Foo createFooInSomeWay() {
// can be a static method as well,
// if we don't need the factory to provide its own object semantics
// and just serve as a group of methods
return new Foo(some, args);
}
}
সি ++ এ এটি অনুবাদ করে:
class FooFactory {
public:
Foo* createFooInSomeWay() {
return new Foo(some, args);
}
};
Cool? প্রায়শই, সত্যই। তবে তারপরে - এটি ব্যবহারকারীকে কেবল গতিশীল বরাদ্দ ব্যবহার করতে বাধ্য করে। স্ট্যাটিক বরাদ্দকরণ যা সি ++ জটিল করে তোলে তা হ'ল এটি প্রায়শই শক্তিশালী করে তোলে। এছাড়াও, আমি বিশ্বাস করি যে এখানে কিছু লক্ষ্যমাত্রা রয়েছে (কীওয়ার্ড: এম্বেড করা) যা গতিশীল বরাদ্দের অনুমতি দেয় না। এবং এটি বোঝায় না যে এই প্ল্যাটফর্মগুলির ব্যবহারকারীরা ক্লিন ওওপি লিখতে পছন্দ করেন।
যাইহোক, দর্শন একপাশে: সাধারণ ক্ষেত্রে, আমি কারখানার ব্যবহারকারীদের গতিশীল বরাদ্দের জন্য সীমাবদ্ধ রাখতে বাধ্য করতে চাই না।
2) রিটার্ন বাই মান
ঠিক আছে, সুতরাং আমরা জানি যে 1) শীতল যখন আমরা গতিশীল বরাদ্দ চাই। কেন আমরা তার উপরে স্থির বরাদ্দ যোগ করব না?
class FooFactory {
public:
Foo* createFooInSomeWay() {
return new Foo(some, args);
}
Foo createFooInSomeWay() {
return Foo(some, args);
}
};
কি? আমরা রিটার্ন টাইপ দ্বারা ওভারলোড করতে পারি না? ওহ, আমরা অবশ্যই পারি না সুতরাং এর প্রতিফলিত করার জন্য পদ্ধতি নাম পরিবর্তন করা যাক। এবং হ্যাঁ, আমি পদ্ধতির নাম পরিবর্তন করার প্রয়োজনীয়তাটি কতটা অপছন্দ করি তা বোঝাতে আমি উপরে অবৈধ কোড উদাহরণটি লিখেছি, উদাহরণস্বরূপ, কারণ আমরা এখন কোনও ভাষা-অজ্ঞাত কারখানার নকশা সঠিকভাবে প্রয়োগ করতে পারি না, যেহেতু আমাদের নাম পরিবর্তন করতে হবে - এবং এই কোডটির প্রত্যেক ব্যবহারকারীর স্পেসিফিকেশন থেকে বাস্তবায়নের সেই তফাতটি মনে রাখা দরকার।
class FooFactory {
public:
Foo* createDynamicFooInSomeWay() {
return new Foo(some, args);
}
Foo createFooObjectInSomeWay() {
return Foo(some, args);
}
};
ঠিক আছে ... ওখানে আমাদের আছে। এটি কুরুচিপূর্ণ, কারণ আমাদের পদ্ধতির নাম পরিবর্তন করতে হবে। এটি অসম্পূর্ণ, যেহেতু আমাদের একই কোডটি দু'বার লিখতে হবে। তবে একবার হয়ে গেলে তা কাজ করে। রাইট?
ভাল, সাধারণত। তবে কখনও কখনও তা হয় না। ফু তৈরি করার সময়, আমরা প্রকৃতপক্ষে আমাদের জন্য রিটার্ন মান অপ্টিমাইজেশন করতে সংকলকটির উপর নির্ভর করি কারণ কম্পাইলার বিক্রেতাদের জন্য নির্দিষ্ট করে না যে সি ++ স্ট্যান্ডার্ডটি কখন বস্তুটি স্থানে তৈরি করবে এবং কখন প্রত্যাবর্তন করার সময় এটি অনুলিপি করা হবে সি ++ এ মান অনুসারে অস্থায়ী বস্তু। সুতরাং যদি ফু কপি করা ব্যয়বহুল হয় তবে এই পদ্ধতির ঝুঁকিপূর্ণ।
এবং যদি ফু আদৌ অনুলিপিযোগ্য না হয় তবে কী হবে? ঠিক আছে, দোহ। ( দ্রষ্টব্য যে গ্যারান্টিযুক্ত অনুলিপি এলিসি সহ সি ++ 17 এ, উপরের কোডটির জন্য অনুলিপি-অনুলিপি এখন আর কোনও সমস্যা নয় )
উপসংহার: কোনও জিনিস ফেরত দিয়ে কারখানা তৈরি করা কিছু ক্ষেত্রে অবশ্যই সমাধান (যেমন পূর্বে উল্লিখিত 2-ডি ভেক্টর), তবে এখনও নির্মাণকারীদের জন্য সাধারণ প্রতিস্থাপন নয়।
3) দ্বি-ফেজ নির্মাণ
আরেকটি জিনিস যা সম্ভবত কেউ সামনে আসবে তা হ'ল অবজেক্ট বরাদ্দ এবং এর সূচনাকরণের বিষয়টি পৃথক করে। এটি সাধারণত কোডের মতো ফলাফল দেয়:
class Foo {
public:
Foo() {
// empty or almost empty
}
// ...
};
class FooFactory {
public:
void createFooInSomeWay(Foo& foo, some, args);
};
void clientCode() {
Foo staticFoo;
auto_ptr<Foo> dynamicFoo = new Foo();
FooFactory factory;
factory.createFooInSomeWay(&staticFoo);
factory.createFooInSomeWay(&dynamicFoo.get());
// ...
}
কেউ ভাবতে পারেন এটি আকর্ষণীয় কাজ করে। আমাদের কোডে আমরা একমাত্র মূল্য প্রদান করি ...
যেহেতু আমি এগুলি সমস্ত লিখেছি এবং এটিকে শেষ হিসাবে রেখেছি তাই আমাকে অবশ্যই এটি অপছন্দ করতে হবে। :) কেন?
প্রথমত ... আমি দ্বি-ফেজ নির্মাণের ধারণাটি আন্তরিকভাবে অপছন্দ করি এবং আমি এটি ব্যবহার করার সময় নিজেকে দোষী মনে করি। যদি আমি "যদি এটি বিদ্যমান থাকে তবে এটি বৈধ অবস্থায় রয়েছে" এই দৃ with় প্রতিবেদনের সাথে আমি যদি আমার অবজেক্টগুলি ডিজাইন করি তবে আমি অনুভব করি যে আমার কোডটি নিরাপদ এবং কম ত্রুটিযুক্ত। আমি এইটা এইভাবেই পছন্দ করি.
এই কনভেনশনটি ফেলে দেওয়া এবং আমার উদ্দেশ্যটির নকশা পরিবর্তন করা কেবল এটির কারখানা তৈরির উদ্দেশ্যে well
আমি জানি যে উপরেরগুলি অনেক লোককে বোঝায় না, তাই আসুন আমি আরও কিছু দৃ argu় যুক্তি দিই। দ্বি-ফেজ নির্মাণ ব্যবহার করে, আপনি পারবেন না:
- সূচনা
const
বা রেফারেন্স সদস্য ভেরিয়েবল, - বেস ক্লাস কনস্ট্রাক্টর এবং সদস্য অবজেক্ট কনস্ট্রাক্টরের পক্ষে যুক্তিগুলি পাস করুন।
এবং সম্ভবত আরও কিছু ত্রুটি থাকতে পারে যা আমি এই মুহূর্তে ভাবতে পারি না এবং উপরের বুলেট পয়েন্টগুলি ইতিমধ্যে আমাকে বোঝানোর কারণে আমি বিশেষভাবে বাধ্যও বোধ করি না।
সুতরাং: এমনকি কারখানা বাস্তবায়নের জন্য কোনও ভাল সাধারণ সমাধানের কাছাকাছি নয়।
উপসংহার:
আমরা অবজেক্ট ইনস্ট্যান্টেশন করার একটি উপায় রাখতে চাই যা হ'ল:
- বরাদ্দ নির্বিশেষে অভিন্ন তাত্পর্য জন্য অনুমতি দিন,
- নির্মাণ পদ্ধতিগুলিতে পৃথক, অর্থপূর্ণ নাম দিন (এভাবে বাই-তর্ক ওভারলোডিংয়ের উপর নির্ভর করে না),
- কোনও উল্লেখযোগ্য পারফরম্যান্স হিট এবং, বিশেষত ক্লায়েন্টের পাশে, একটি উল্লেখযোগ্য কোড ব্লাট হিট প্রবর্তন করবেন না,
- সাধারণ হন, যেমন: যে কোনও শ্রেণির জন্য প্রবর্তন করা সম্ভব।
আমি বিশ্বাস করি যে আমি প্রমাণ করেছি যে আমি যেভাবে উল্লেখ করেছি সেগুলি সেই প্রয়োজনীয়তা পূরণ করে না।
কোন ইঙ্গিত? দয়া করে আমাকে একটি সমাধান সরবরাহ করুন, আমি ভাবতে চাই না যে এই ভাষা আমাকে এ জাতীয় তুচ্ছ ধারণাটি সঠিকভাবে প্রয়োগ করতে দেয় না।
delete
। এই ধরণের পদ্ধতিগুলি পুরোপুরি সূক্ষ্ম, যতক্ষণ না এটি "ডকুমেন্টেড" (সোর্স কোড ডকুমেন্টেশন ;-)) থাকে যে কলার পয়েন্টারের মালিকানা গ্রহণ করে (পড়ুন: উপযুক্ত হলে এটি মোছার জন্য দায়বদ্ধ)।
unique_ptr<T>
পরিবর্তে ফিরে এসে এটি খুব স্পষ্ট করে তুলতে পারেন T*
।