কীভাবে সি ++ এ কারখানার পদ্ধতি প্যাটার্নটি সঠিকভাবে প্রয়োগ করা যায়


329

সি ++ তে এই একটি জিনিস রয়েছে যা আমাকে দীর্ঘদিন ধরে অস্বস্তি বোধ করে চলেছে, কারণ এটি সহজ বলে মনে হলেও আমি কীভাবে এটি করতে তা সত্যতার সাথে জানি না:

আমি কীভাবে সি ++ এ কারখানার পদ্ধতি কার্যকর করব?

লক্ষ্য: অগ্রহণযোগ্য পরিণতি এবং পারফরম্যান্সের আঘাত ছাড়াই ক্লায়েন্টকে অবজেক্টের কনস্ট্রাক্টরের পরিবর্তে কারখানার পদ্ধতিগুলি ব্যবহার করে কিছু অবজেক্ট ইনস্ট্যান্ট করার অনুমতি দেওয়া সম্ভব করে তোলে।

"ফ্যাক্টরি মেথড প্যাটার্ন" দ্বারা, আমি উভয় স্থিতিশীল কারখানার পদ্ধতি কোনও অবজেক্টের ভিতরে বা অন্য শ্রেণিতে সংজ্ঞায়িত পদ্ধতিগুলি বা বৈশ্বিক ফাংশনগুলিকে বোঝাতে চাইছি। কেবলমাত্র "দশম শ্রেণির ইনস্ট্যান্টেশনের স্বাভাবিক পদ্ধতিটি কনস্ট্রাক্টর বাদে অন্য কোথাও পুনর্নির্দেশের ধারণা"।

আমি কিছু সম্ভাব্য উত্তর যা আমি ভেবে দেখেছি তা স্কিম করতে দিন।


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বা রেফারেন্স সদস্য ভেরিয়েবল,
  • বেস ক্লাস কনস্ট্রাক্টর এবং সদস্য অবজেক্ট কনস্ট্রাক্টরের পক্ষে যুক্তিগুলি পাস করুন।

এবং সম্ভবত আরও কিছু ত্রুটি থাকতে পারে যা আমি এই মুহূর্তে ভাবতে পারি না এবং উপরের বুলেট পয়েন্টগুলি ইতিমধ্যে আমাকে বোঝানোর কারণে আমি বিশেষভাবে বাধ্যও বোধ করি না।

সুতরাং: এমনকি কারখানা বাস্তবায়নের জন্য কোনও ভাল সাধারণ সমাধানের কাছাকাছি নয়।


উপসংহার:

আমরা অবজেক্ট ইনস্ট্যান্টেশন করার একটি উপায় রাখতে চাই যা হ'ল:

  • বরাদ্দ নির্বিশেষে অভিন্ন তাত্পর্য জন্য অনুমতি দিন,
  • নির্মাণ পদ্ধতিগুলিতে পৃথক, অর্থপূর্ণ নাম দিন (এভাবে বাই-তর্ক ওভারলোডিংয়ের উপর নির্ভর করে না),
  • কোনও উল্লেখযোগ্য পারফরম্যান্স হিট এবং, বিশেষত ক্লায়েন্টের পাশে, একটি উল্লেখযোগ্য কোড ব্লাট হিট প্রবর্তন করবেন না,
  • সাধারণ হন, যেমন: যে কোনও শ্রেণির জন্য প্রবর্তন করা সম্ভব।

আমি বিশ্বাস করি যে আমি প্রমাণ করেছি যে আমি যেভাবে উল্লেখ করেছি সেগুলি সেই প্রয়োজনীয়তা পূরণ করে না।

কোন ইঙ্গিত? দয়া করে আমাকে একটি সমাধান সরবরাহ করুন, আমি ভাবতে চাই না যে এই ভাষা আমাকে এ জাতীয় তুচ্ছ ধারণাটি সঠিকভাবে প্রয়োগ করতে দেয় না।


7
@ জ্যাক, শিরোনামটি খুব মিল থাকলেও আসল প্রশ্নগুলি আইএমএইচও থেকে আলাদা।
পিয়েটার টার্ক

2
ভাল সদৃশ তবে এই প্রশ্নের পাঠ্যটি নিজেই এবং মূল্যবান।
dmckee --- প্রাক্তন-মডারেটর বিড়ালছানা

7
এটি জিজ্ঞাসার দু'বছর পরে আমার কিছু যুক্তি যুক্ত করতে হবে: 1) এই প্রশ্নটি বেশ কয়েকটি ডিজাইনের ধরণের ([বিমূর্ত] কারখানার, নির্মাতা, আপনি নামটি দিয়েছিলেন, এটির নাম রাখুন, আমি তাদের শ্রবণশৈলীতে পছন্দ করি না) পছন্দ করি। ২) এখানে আসল বিষয়টি নিয়ে আলোচনা করা হচ্ছে "কীভাবে অবজেক্ট কনস্ট্রাকশন থেকে অবজেক্ট স্টোরেজ বরাদ্দকে পরিষ্কারভাবে ডিকুয়াল করা যায়?"
কোস

1
@ ডেনিস: আপনি যদি তা না করেন তবেই delete। এই ধরণের পদ্ধতিগুলি পুরোপুরি সূক্ষ্ম, যতক্ষণ না এটি "ডকুমেন্টেড" (সোর্স কোড ডকুমেন্টেশন ;-)) থাকে যে কলার পয়েন্টারের মালিকানা গ্রহণ করে (পড়ুন: উপযুক্ত হলে এটি মোছার জন্য দায়বদ্ধ)।
বরিস ডালস্টাইন

1
@ বরিস @ ডেনিস আপনি এটির unique_ptr<T>পরিবর্তে ফিরে এসে এটি খুব স্পষ্ট করে তুলতে পারেন T*
কোস

উত্তর:


107

প্রথমত, কিছু ক্ষেত্রে রয়েছে যখন অবজেক্ট নির্মাণ একটি টাস্ক জটিল হিসাবে এটি অন্যের ক্লাসে নিষ্কাশনকে ন্যায়সঙ্গত করতে পারে।

আমি বিশ্বাস করি এই বিষয়টি ভুল is জটিলতা আসলে কিছু যায় আসে না। প্রাসঙ্গিকতা হয় কি। যদি কোনও পদক্ষেপ এক ধাপে তৈরি করা যায় (বিল্ডার প্যাটার্নের মতো নয়) তবে এটির জন্য নির্মাতা সঠিক জায়গা। কাজটি সম্পাদন করতে যদি আপনার সত্যই অন্য ক্লাসের প্রয়োজন হয়, তবে এটি হেল্পার ক্লাস হওয়া উচিত যা কনস্ট্রাক্টর থেকে যেভাবেই ব্যবহৃত হয়।

Vec2(float x, float y);
Vec2(float angle, float magnitude); // not a valid overload!

এটির জন্য একটি সহজ কাজ রয়েছে:

struct Cartesian {
  inline Cartesian(float x, float y): x(x), y(y) {}
  float x, y;
};
struct Polar {
  inline Polar(float angle, float magnitude): angle(angle), magnitude(magnitude) {}
  float angle, magnitude;
};
Vec2(const Cartesian &cartesian);
Vec2(const Polar &polar);

একমাত্র অসুবিধা হ'ল এটি কিছুটা ভার্বোস দেখাচ্ছে:

Vec2 v2(Vec2::Cartesian(3.0f, 4.0f));

তবে ভাল কথাটি হ'ল আপনি অবিলম্বে দেখতে পাচ্ছেন আপনি কী স্থানাঙ্ক ব্যবহার করছেন এবং একই সময়ে আপনাকে অনুলিপি করার বিষয়ে চিন্তা করতে হবে না। আপনি যদি অনুলিপি করতে চান এবং এটি ব্যয়বহুল (অবশ্যই প্রোফাইলিং দ্বারা প্রমাণিত), আপনি কিউটি-র ভাগ করা শ্রেণীর মতো কিছু ব্যবহার করতে ইচ্ছুক হতে পারেন ওভারহেড অনুলিপি এড়াতে ।

বরাদ্দ ধরণের হিসাবে, কারখানার প্যাটার্নটি ব্যবহারের প্রধান কারণটি সাধারণত পলিমারফিজম। কনস্ট্রাক্টরগুলি ভার্চুয়াল হতে পারে না, এবং তারা পারলেও এটি খুব একটা বোঝায় না। স্ট্যাটিক বা স্ট্যাক বরাদ্দ ব্যবহার করার সময়, আপনি বহুবর্ষীয় উপায়ে অবজেক্ট তৈরি করতে পারবেন না কারণ সংকলকটির সঠিক আকারটি জানতে হবে। সুতরাং এটি কেবল পয়েন্টার এবং রেফারেন্স সহ কাজ করে। আর একটি কারখানা থেকে একটি রেফারেন্স ফেরত খুব কাজ করে না, কারণ একটি বস্তুর টেকনিক্যালি যখন যা করতে পারেন রেফারেন্স দ্বারা মুছে ফেলা হতে, বরং এটা হতে বিভ্রান্তিকর ও বাগ-প্রবণ, দেখতে পারে একটি সি ++ রেফারেন্স পরিবর্তনশীল, মন্দ ফিরে চর্চা আছে কি?উদাহরণ স্বরূপ. সুতরাং পয়েন্টারগুলি হ'ল একমাত্র জিনিস যা বাকি আছে এবং এতে স্মার্ট পয়েন্টারও রয়েছে। অন্য কথায়, গতিশীল বরাদ্দের সাথে কারখানাগুলি ব্যবহার করা সর্বাধিক কার্যকর, সুতরাং আপনি এই জাতীয় জিনিসগুলি করতে পারেন:

class Abstract {
  public:
    virtual void do() = 0;
};

class Factory {
  public:
    Abstract *create();
};

Factory f;
Abstract *a = f.create();
a->do();

অন্যান্য ক্ষেত্রে, কারখানাগুলি কেবলমাত্র উল্লিখিত ওভারলোডের মতো ছোটখাটো সমস্যা সমাধানে সহায়তা করে। এগুলি একটি অভিন্ন উপায়ে ব্যবহার করা সম্ভব হলে এটি চমৎকার হবে, তবে এটি এতটা ক্ষতি করে না যে এটি সম্ভবত অসম্ভব।


21
কার্টেসিয়ান এবং পোলার স্ট্রাক্টের জন্য +1। সাধারণত ক্লাস এবং স্ট্রাক্ট তৈরি করা ভাল যা তাদের উদ্দেশ্যে করা ডেটাগুলি সরাসরি উপস্থাপন করে (সাধারণ ভেক স্ট্রাক্টের বিপরীতে)। আপনার কারখানাটিও একটি ভাল উদাহরণ, তবে আপনার উদাহরণটি বোঝায় না যে পয়েন্টারটি 'এ' এর মালিক। যদি কারখানার 'চ' এর মালিকানা থাকে, তবে সম্ভবত 'ফ' এর সুযোগ ছেড়ে দিলে সম্ভবত এটি ধ্বংস হয়ে যায়, তবে 'চ' এর মালিকানা না থাকলে বিকাশকারীকে সেই স্মৃতিটি মুক্ত করা মনে রাখা গুরুত্বপূর্ণ, নাহলে একটি স্মৃতি ফাঁস হতে পারে ঘটতে পারে।
ডেভিড পিটারসন

1
অবশ্যই কোনও বস্তু রেফারেন্স দিয়ে মুছে ফেলা যায়! দেখুন স্ট্যাকওভারফ্লো.com/a/752699/404734 অবশ্যই প্রশ্নটি উত্থাপন করে যদি রেফারেন্স দ্বারা গতিশীল মেমরি ফিরিয়ে দেওয়া বুদ্ধিমানের কারণ, অনুলিপি দ্বারা অনুলিপিটির সাথে প্রত্যাবর্তনের মানকে সম্ভাব্যভাবে বরাদ্দ করার সমস্যাটি রয়েছে (কলকারী অবশ্যই কিছু করতে পারে যেমন int a = * রিটার্নঅ্যাপিনটারটারটাইন্ট () এবং তার পরে একই সমস্যার মুখোমুখি হতে হবে, যদি গতিশীলভাবে অ্যালকোয়েটেড মেমোরি ফিরে আসে, যেমন রেফারেন্সের মতো, তবে পয়েন্টার সংস্করণে ব্যবহারকারীকে স্পষ্টভাবে উল্লেখ করতে ভুলার পরিবর্তে স্পষ্টতই অবলম্বন করতে হবে, ভুল হতে হবে) ।
কাইসারলুদি

1
কাইসারলুদি, চমৎকার পয়েন্ট আমি এটি ভেবে দেখিনি, তবে জিনিসগুলি করার এটি এখনও একটি "দুষ্ট" উপায়। প্রতিফলিত করতে আমার উত্তর সম্পাদনা।
সের্গেই তাচেনভ

অপরিবর্তনীয় বিভিন্ন নন-পলিমারফিক ক্লাস তৈরি সম্পর্কে কী? তখন কি কারখানার প্যাটার্নটি সি ++ এ ব্যবহার করা উপযুক্ত?
ডায়াক্সিক্স

@ ড্যাক্সিক্স, নন-পলিমারফিক ক্লাসের উদাহরণ তৈরি করার জন্য আপনার কেন কারখানার প্রয়োজন হবে? অপরিবর্তনীয়তার এর কোনও কিছুর সাথে কী সম্পর্ক আছে তা আমি দেখতে পাচ্ছি না।
সের্গেই তাচেনভ

49

সাধারণ কারখানার উদাহরণ:

// Factory returns object and ownership
// Caller responsible for deletion.
#include <memory>
class FactoryReleaseOwnership{
  public:
    std::unique_ptr<Foo> createFooInSomeWay(){
      return std::unique_ptr<Foo>(new Foo(some, args));
    }
};

// Factory retains object ownership
// Thus returning a reference.
#include <boost/ptr_container/ptr_vector.hpp>
class FactoryRetainOwnership{
  boost::ptr_vector<Foo>  myFoo;
  public:
    Foo& createFooInSomeWay(){
      // Must take care that factory last longer than all references.
      // Could make myFoo static so it last as long as the application.
      myFoo.push_back(new Foo(some, args));
      return myFoo.back();
    }
};

2
@ লোকিআস্টারি স্মরণশক্তি নিয়ন্ত্রণ নিয়ন্ত্রণের সহজ উপায় হ'ল স্মার্ট পয়েন্টার ব্যবহার। অন্যান্য ভাষার তুলনায় সি / সি ++ ল্যাংসের নিয়ন্ত্রণটি সর্বোচ্চ হিসাবে পরিচিত এবং যার থেকে তারা সবচেয়ে বেশি সুবিধা অর্জন করে। স্মার্ট পয়েন্টারগুলি অন্যান্য পরিচালিত ভাষার মতো মেমরির ওভারহেড উত্পাদন করে mentioning আপনি যদি জাভা বা সি # তে স্বয়ংক্রিয় মেমরি পরিচালনার সুবিধার্থে প্রোগ্রামিং শুরু করতে চান তবে সেই জগাখিচুড়ি সি / সি ++ এ রাখবেন না।
luke1985

45
@ unique_ptrউদাহরণস্বরূপ lukasz1985 এর ওভারহেডের পারফরম্যান্স নেই। মেমোরি সহ রিসোর্সগুলি পরিচালনা, অন্য যে কোনও ভাষার তুলনায় সি ++ এর চূড়ান্ত সুবিধাগুলির মধ্যে একটি কারণ আপনি নিয়ন্ত্রণটি না হারিয়ে কোনও পারফরম্যান্স পেনাল্টি এবং নির্বিচারে এটি করতে পারেন, তবে আপনি ঠিক এর বিপরীতে বলেছেন। কিছু লোক সি ++ স্পষ্টভাবে না করে এমন জিনিসগুলি অপছন্দ করে, যেমন স্মার্ট পয়েন্টারগুলির মাধ্যমে মেমরি পরিচালনা, তবে আপনি যা চান তা যদি সমস্ত কিছু বাধ্যতামূলকভাবে সুস্পষ্টভাবে হয়, তবে সি ব্যবহার করুন; ট্রেডঅফটি হ'ল পরিমাণের কম সমস্যার অর্ডার। আমি মনে করি এটি অন্যায়, আপনি একটি ভাল সুপারিশ বাতিল।
TheCppZoo

1
@ এডমাস্টার: আমি এর আগে কোনও প্রতিক্রিয়া জানাইনি কারণ তিনি অবশ্যই ট্রোলিং করছিলেন। দয়া করে ট্রলটি খাওয়াবেন না।
মার্টিন ইয়র্ক

17
@ লোকি আস্তারি তিনি ট্রল হতে পারেন তবে তিনি যা বলে তা মানুষকে বিভ্রান্ত করতে পারে
TheCppZoo

1
@ ইয়াহ: হ্যাঁ তবে: boost::ptr_vector<>এটি একটি সামান্য বিট আরও দক্ষ কারণ এটি বোঝে যে এটি একটি সাবক্লাসে কাজ অর্পণ করার চেয়ে পয়েন্টারের মালিক। কিন্তু প্রধান সুবিধা boost::ptr_vector<>এটি রেফারেন্স (না পয়েন্টার) দ্বারা তার সদস্যদের অনাবৃত এইভাবে এটা সত্যিই সহজ মান লাইব্রেরিতে আলগোরিদিম সঙ্গে ব্যবহার করার জন্য নয়।
মার্টিন ইয়র্ক

41

আপনি কি কারখানাটি মোটেই ব্যবহার না করা এবং এর পরিবর্তে টাইপ সিস্টেমটির দুর্দান্ত ব্যবহার করার কথা ভেবে দেখেছেন? আমি দুটি ভিন্ন পদ্ধতির কথা ভাবতে পারি যা এই ধরণের কাজ করে:

বিকল্প 1:

struct linear {
    linear(float x, float y) : x_(x), y_(y){}
    float x_;
    float y_;
};

struct polar {
    polar(float angle, float magnitude) : angle_(angle),  magnitude_(magnitude) {}
    float angle_;
    float magnitude_;
};


struct Vec2 {
    explicit Vec2(const linear &l) { /* ... */ }
    explicit Vec2(const polar &p) { /* ... */ }
};

যা আপনাকে এই জাতীয় জিনিস লিখতে দেয়:

Vec2 v(linear(1.0, 2.0));

বিকল্প 2:

আপনি "ট্যাগ" ব্যবহার করতে পারেন যেমন এসটিএল পুনরাবৃত্তির সাথে এবং এর মতো করে। উদাহরণ স্বরূপ:

struct linear_coord_tag linear_coord {}; // declare type and a global
struct polar_coord_tag polar_coord {};

struct Vec2 {
    Vec2(float x, float y, const linear_coord_tag &) { /* ... */ }
    Vec2(float angle, float magnitude, const polar_coord_tag &) { /* ... */ }
};

এই দ্বিতীয় পদ্ধতির সাহায্যে আপনাকে কোডটি লিখতে দেয় যা দেখতে এরকম দেখাচ্ছে:

Vec2 v(1.0, 2.0, linear_coord);

যা আপনাকে প্রতিটি নির্মাণকারীর জন্য অনন্য প্রোটোটাইপ রাখার অনুমতি দেওয়ার সময়ও দুর্দান্ত এবং অভিব্যক্তিপূর্ণ।


29

আপনি এখানে একটি খুব ভাল সমাধান পড়তে পারেন: http://www.codeproject.com/Articles/363338/ কারখানার-প্যাটার্ন- ইন- ক্লিপপ্লাস

সর্বোত্তম সমাধানটি "মন্তব্য এবং আলোচনা" এ, "স্ট্যাটিক তৈরি পদ্ধতিগুলির প্রয়োজন নেই" দেখুন।

এই ধারণা থেকে, আমি একটি কারখানা করেছি। মনে রাখবেন যে আমি কিউটি ব্যবহার করছি তবে আপনি স্ট্যান্ড সমতুল্যদের জন্য কিউম্যাপ এবং কিউ স্ট্রিং পরিবর্তন করতে পারেন।

#ifndef FACTORY_H
#define FACTORY_H

#include <QMap>
#include <QString>

template <typename T>
class Factory
{
public:
    template <typename TDerived>
    void registerType(QString name)
    {
        static_assert(std::is_base_of<T, TDerived>::value, "Factory::registerType doesn't accept this type because doesn't derive from base class");
        _createFuncs[name] = &createFunc<TDerived>;
    }

    T* create(QString name) {
        typename QMap<QString,PCreateFunc>::const_iterator it = _createFuncs.find(name);
        if (it != _createFuncs.end()) {
            return it.value()();
        }
        return nullptr;
    }

private:
    template <typename TDerived>
    static T* createFunc()
    {
        return new TDerived();
    }

    typedef T* (*PCreateFunc)();
    QMap<QString,PCreateFunc> _createFuncs;
};

#endif // FACTORY_H

নমুনা ব্যবহার:

Factory<BaseClass> f;
f.registerType<Descendant1>("Descendant1");
f.registerType<Descendant2>("Descendant2");
Descendant1* d1 = static_cast<Descendant1*>(f.create("Descendant1"));
Descendant2* d2 = static_cast<Descendant2*>(f.create("Descendant2"));
BaseClass *b1 = f.create("Descendant1");
BaseClass *b2 = f.create("Descendant2");

17

আমি বেশিরভাগ গ্রহণযোগ্য উত্তরের সাথে একমত, তবে একটি সি ++ 11 বিকল্প রয়েছে যা বিদ্যমান উত্তরগুলিতে আবৃত হয়নি:

  • মান দ্বারা কারখানা পদ্ধতির ফলাফলগুলি ফেরত দিন এবং
  • একটি সস্তা সরানো কনস্ট্রাক্টর সরবরাহ করুন

উদাহরণ:

struct sandwich {
  // Factory methods.
  static sandwich ham();
  static sandwich spam();
  // Move constructor.
  sandwich(sandwich &&);
  // etc.
};

তারপরে আপনি স্ট্যাকের উপর অবজেক্টগুলি তৈরি করতে পারেন:

sandwich mine{sandwich::ham()};

অন্যান্য বিষয়গুলির সাবওবজেক্ট হিসাবে:

auto lunch = std::make_pair(sandwich::spam(), apple{});

বা গতিশীলভাবে বরাদ্দ:

auto ptr = std::make_shared<sandwich>(sandwich::ham());

আমি কখন এটি ব্যবহার করতে পারি?

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

আমি বলতে ' যথাসাধ্য ' কারণ এটা নির্ভর করে যার উপর পদ্ধতির অকারণে অদক্ষ ছাড়া স্পষ্ট কোড দেয়।


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

11

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


1
এমনকি যদি এটি পুরানো হয় (যা আমি বিতর্ক করি) তবে এটি এখনও পুরোপুরি পরিষেবাযোগ্য। আমি এখনও নতুন সি ++ 14 প্রকল্পে এমসি ++ ডি এর উপর ভিত্তি করে একটি কারখানা ব্যবহার করছি! তদুপরি, কারখানা এবং সিঙ্গলটন নিদর্শনগুলি সম্ভবত সবচেয়ে কম-পুরানো অংশ। মত লোকি টুকরা যদিও Functionলিখুন হেরফেরের প্রতিস্থাপিত হতে পারে সঙ্গে std::functionএবং <type_traits>এবং যখন lambdas, থ্রেডিং, rvalue refs প্রভাব যে কিছু ছোটখাট টোয়েকিং প্রয়োজন হতে পারে আছে, কারখানার singletons জন্য কোন মান প্রতিস্থাপন হিসাবে তিনি তাদের বর্ণনা করা হয়েছে।
ধাতু

5

আমি আমার সমস্ত প্রশ্নের উত্তর দেওয়ার চেষ্টা করি না, কারণ আমি বিশ্বাস করি এটি খুব বিস্তৃত। মাত্র কয়েকটি নোট:

এমন কিছু বিষয় রয়েছে যখন অবজেক্ট কনস্ট্রাকশন অন্য শ্রেণীর কাছে এর নিষ্কাশনকে ন্যায়সঙ্গত করার পক্ষে যথেষ্ট জটিল একটি কাজ জটিল।

এই শ্রেণিটি আসলে একটি কারখানার পরিবর্তে একজন নির্মাতা

সাধারণ ক্ষেত্রে, আমি কারখানার ব্যবহারকারীদের গতিশীল বরাদ্দে সীমাবদ্ধ রাখতে বাধ্য করতে চাই না।

তারপরে আপনি আপনার ফ্যাক্টরিটিকে এটি একটি স্মার্ট পয়েন্টারে আবদ্ধ করতে পারেন। আমি বিশ্বাস করি এইভাবে আপনি আপনার কেক রাখতে পারেন এবং এটিও খেতে পারেন।

এটি রিটার্ন-বাই-মান সম্পর্কিত সমস্যাগুলিও সরিয়ে দেয়।

উপসংহার: কোনও জিনিস ফেরত দিয়ে কারখানা তৈরি করা কিছু ক্ষেত্রে অবশ্যই সমাধান (যেমন পূর্বে উল্লিখিত 2-ডি ভেক্টর), তবে এখনও নির্মাণকারীদের জন্য সাধারণ প্রতিস্থাপন নয়।

প্রকৃতপক্ষে. সমস্ত ডিজাইনের ধরণগুলিতে তাদের (ভাষা নির্দিষ্ট) সীমাবদ্ধতা এবং ত্রুটি রয়েছে। এগুলি কেবল তখনই ব্যবহার করার পরামর্শ দেওয়া হয় যখন তারা আপনাকে তাদের সমস্যার জন্য নয়, আপনার সমস্যা সমাধানে সহায়তা করে।

আপনি যদি "নিখুঁত" কারখানা বাস্তবায়নের পরে থাকেন তবে ভাল, ভাগ্য luck


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

@ কোস, স্মার্ট পয়েন্টার সহ আপনি আপনার ব্যবহারকারীর কাছ থেকে প্রকৃত বস্তুর বরাদ্দ / অবনতি গোপন করতে পারবেন। তারা কেবল এনক্যাপসুলেটিং স্মার্ট পয়েন্টার দেখতে পায়, যা বাইরের বিশ্বের সাথে স্থিতি বরাদ্দকৃত বস্তুর মতো আচরণ করে।
পিয়েটার টার্ক

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

3

এটি আমার সি ++ 11 স্টাইলের সমাধান। প্যারামিটার 'বেস' সমস্ত সাব-ক্লাসের বেস ক্লাসের জন্য। নির্মাতারা, এসটিডি :: ফাংশন অবজেক্টগুলি উপ-শ্রেণীর উদাহরণগুলি তৈরি করতে পারে, এটি আপনার সাব-ক্লাসের 'স্ট্যাটিক সদস্য ফাংশন' তৈরি (কিছু আরগস) 'এর জন্য বাধ্যতামূলক হতে পারে। এটি সম্ভবত নিখুঁত নয় তবে আমার পক্ষে কাজ করে। এবং এটি 'সাধারণ' সমাধান।

template <class base, class... params> class factory {
public:
  factory() {}
  factory(const factory &) = delete;
  factory &operator=(const factory &) = delete;

  auto create(const std::string name, params... args) {
    auto key = your_hash_func(name.c_str(), name.size());
    return std::move(create(key, args...));
  }

  auto create(key_t key, params... args) {
    std::unique_ptr<base> obj{creators_[key](args...)};
    return obj;
  }

  void register_creator(const std::string name,
                        std::function<base *(params...)> &&creator) {
    auto key = your_hash_func(name.c_str(), name.size());
    creators_[key] = std::move(creator);
  }

protected:
  std::unordered_map<key_t, std::function<base *(params...)>> creators_;
};

ব্যবহার সম্পর্কে একটি উদাহরণ।

class base {
public:
  base(int val) : val_(val) {}

  virtual ~base() { std::cout << "base destroyed\n"; }

protected:
  int val_ = 0;
};

class foo : public base {
public:
  foo(int val) : base(val) { std::cout << "foo " << val << " \n"; }

  static foo *create(int val) { return new foo(val); }

  virtual ~foo() { std::cout << "foo destroyed\n"; }
};

class bar : public base {
public:
  bar(int val) : base(val) { std::cout << "bar " << val << "\n"; }

  static bar *create(int val) { return new bar(val); }

  virtual ~bar() { std::cout << "bar destroyed\n"; }
};

int main() {
  common::factory<base, int> factory;

  auto foo_creator = std::bind(&foo::create, std::placeholders::_1);
  auto bar_creator = std::bind(&bar::create, std::placeholders::_1);

  factory.register_creator("foo", foo_creator);
  factory.register_creator("bar", bar_creator);

  {
    auto foo_obj = std::move(factory.create("foo", 80));
    foo_obj.reset();
  }

  {
    auto bar_obj = std::move(factory.create("bar", 90));
    bar_obj.reset();
  }
}

ভাল লাগছে আমার কাছে। আপনি কীভাবে বাস্তবায়ন করবেন (সম্ভবত কিছু ম্যাক্রো যাদু) স্থিতিশীল নিবন্ধকরণ? বেস ক্লাসটি অবজেক্টগুলির জন্য কিছু সার্ভিসিং ক্লাস হিসাবে কল্পনা করুন। উত্পন্ন ক্লাসগুলি those অবজেক্টগুলিকে বিশেষ ধরণের পরিষেবা প্রদান করে। এবং আপনি ক্রমান্বয়ে সেই ধরণের প্রতিটি পরিষেবার জন্য বেস থেকে প্রাপ্ত একটি শ্রেণি যুক্ত করে বিভিন্ন ধরণের পরিষেবা যুক্ত করতে চান।
St0fF

2

কারখানার প্যাটার্ন

class Point
{
public:
  static Point Cartesian(double x, double y);
private:
};

এবং আপনি যদি সংকলক রিটার্ন মান অপ্টিমাইজেশন সমর্থন করে না, এটি খনন, এটি সম্ভবত খুব বেশি অপ্টিমাইজেশন ধারণ করে না ...


এটিকে কি সত্যিই কারখানার নিদর্শনগুলির একটি বাস্তবায়ন হিসাবে বিবেচনা করা যেতে পারে?
ডেনিস

1
@ ডেনিস: অধঃপতিত মামলা হিসাবে, আমিও তাই ভাবব। সমস্যাটি Factoryহ'ল এটি বেশ জেনেরিক এবং প্রচুর জমিটি জুড়ে; একটি কারখানা যুক্তি যুক্ত করতে পারে (পরিবেশ / সেটআপের উপর নির্ভর করে) বা উদাহরণস্বরূপ কিছু ফ্লাওয়েট / পুল সম্পর্কিত ক্যাচিং সরবরাহ করতে পারে, তবে এই ক্ষেত্রে কিছু পরিস্থিতিতে কেবল তা বোঝা যায়।
ম্যাথিউ এম।

কেবলমাত্র সংকলক পরিবর্তন করা যদি আপনি এটি সাউন্ড করেন ঠিক তত সহজ হবে :)
রোজিনা

@ ফ্রজিনা: :) এটি লিনাক্সে ভাল কাজ করে (জিসিসি / ঝনঝন উল্লেখযোগ্যভাবে সামঞ্জস্যপূর্ণ); আমি স্বীকার করি উইন্ডোজটি এখনও তুলনামূলকভাবে বন্ধ রয়েছে, যদিও এটি 64৪-বিট প্ল্যাটফর্মে ভাল হওয়া উচিত (পথে কম পেটেন্টস, যদি আমি সঠিকভাবে মনে করি তবে)।
ম্যাথিউ এম।

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

1

আমি জানি এই প্রশ্নের উত্তর 3 বছর আগে দেওয়া হয়েছিল, তবে এটি আপনি যা খুঁজছিলেন তা হতে পারে।

গুগল কয়েক সপ্তাহ আগে একটি লাইব্রেরি প্রকাশ করেছে যাতে সহজ এবং নমনীয় গতিশীল বস্তুর বরাদ্দ দেয়। এটি এখানে: http://google-opensource.blogspot.fr/2014/01/introducing-infact-library.html

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.