C ++: ชั้นเรียนควรเป็นเจ้าของหรือสังเกตการอ้างอิงของมัน?


16

บอกว่าผมได้เรียนFoobarที่ใช้ (ขึ้นอยู่กับ) Widgetระดับ ในวันเฒ่าที่ดีWidgetwolud จะถูกประกาศให้เป็นสนามในFoobarหรืออาจจะเป็นตัวชี้ที่ฉลาดถ้าต้องการพฤติกรรม polymorphic และมันจะเริ่มต้นได้ใน constructor:

class Foobar {
    Widget widget;
    public:
    Foobar() : widget(blah blah blah) {}
    // or
    std::unique_ptr<Widget> widget;
    public:
    Foobar() : widget(std::make_unique<Widget>(blah blah blah)) {}
    (…)
};

และเราจะพร้อมทุกอย่างให้เรียบร้อย น่าเสียดายที่วันนี้เด็ก ๆ จาวาจะหัวเราะเยาะเราเมื่อพวกเขาเห็นมันและอย่างถูกต้องตามที่คู่รักFoobarและWidgetร่วมกัน วิธีแก้ปัญหานั้นง่ายมาก: ใช้การฉีดพึ่งพาเพื่อสร้างการพึ่งพาจากFoobarชั้นเรียน แต่แล้ว C ++ ก็บังคับให้เราคิดถึงการเป็นเจ้าของการพึ่งพา การแก้ปัญหาทั้งสามอยู่ในใจ:

ตัวชี้ที่ไม่ซ้ำ

class Foobar {
    std::unique_ptr<Widget> widget;
    public:
    Foobar(std::unique_ptr<Widget> &&w) : widget(w) {}
    (…)
}

Foobarอ้างสิทธิ์ความเป็นเจ้าของ แต่เพียงผู้เดียวของWidgetที่ถูกส่งผ่านไป นี่มีข้อดีดังต่อไปนี้:

  1. ผลกระทบต่อประสิทธิภาพมีน้อยมาก
  2. มันปลอดภัยเนื่องจากFoobarสามารถควบคุมอายุการใช้งานWidgetได้ดังนั้นจึงมั่นใจได้ว่าWidgetจะไม่หายไปทันที
  3. ปลอดภัยที่Widgetจะไม่รั่วไหลและจะถูกทำลายอย่างเหมาะสมเมื่อไม่ต้องการใช้อีกต่อไป

อย่างไรก็ตามสิ่งนี้มีค่าใช้จ่าย:

  1. มันวางข้อ จำกัด เกี่ยวกับวิธีการใช้งานWidgetอินสแตนซ์เช่นไม่มีการจัดสรรสแต็กWidgetsสามารถใช้ไม่Widgetสามารถใช้ร่วมกัน

ตัวชี้ที่ใช้ร่วมกัน

class Foobar {
    std::shared_ptr<Widget> widget;
    public:
    Foobar(const std::shared_ptr<Widget> &w) : widget(w) {}
    (…)
}

นี่อาจใกล้เคียงที่สุดกับ Java และภาษาที่รวบรวมขยะอื่น ๆ ข้อดี:

  1. เป็นสากลมากขึ้นเพราะจะช่วยให้การแบ่งปันการพึ่งพา
  2. รักษาความปลอดภัย (จุด 2 และ 3) ของunique_ptrการแก้ปัญหา

ข้อเสีย:

  1. สิ้นเปลืองทรัพยากรเมื่อไม่มีการแบ่งปัน
  2. ยังคงต้องการการจัดสรรฮีปและไม่อนุญาตวัตถุที่จัดสรรสแต็ก

ตัวชี้การสังเกตเฒ่าล้วน

class Foobar {
    Widget *widget;
    public:
    Foobar(Widget *w) : widget(w) {}
    (…)
}

วางตัวชี้ดิบไว้ในชั้นเรียนและเปลี่ยนภาระการเป็นเจ้าของให้คนอื่น ข้อดี:

  1. ง่ายๆเท่าที่จะทำได้
  2. Widgetยูนิเวอร์แซยอมรับเพียงใด

จุดด้อย:

  1. ไม่ปลอดภัยอีกต่อไป
  2. เปิดตัวอีกกิจการหนึ่งที่รับผิดชอบในการเป็นเจ้าของของทั้งสองและFoobarWidget

แม่แบบการเขียนโปรแกรมบ้า ๆ บอ ๆ

ข้อดีอย่างเดียวที่ฉันคิดได้ก็คือฉันสามารถอ่านหนังสือทั้งหมดที่ฉันไม่ได้หาเวลาขณะที่ซอฟต์แวร์กำลังสร้าง;)

ฉันเอนสู่โซลูชันที่สามเนื่องจากเป็นสากลมากที่สุดมีบางสิ่งที่ต้องจัดการFoobarsต่อไปดังนั้นการจัดการจึงWidgetsเป็นการเปลี่ยนแปลงที่ง่าย อย่างไรก็ตามการใช้ตัวชี้แบบดิบทำให้ฉันรำคาญใจในทางกลับกันตัวเลือกสมาร์ทพอยต์รู้สึกผิดกับฉันเพราะพวกเขาทำให้ผู้บริโภคที่พึ่งพิง จำกัด การสร้างการพึ่งพานั้น

ฉันพลาดอะไรไปรึเปล่า? หรือเพียงแค่การพึ่งพาการฉีดใน C ++ ไม่ใช่เรื่องไม่สำคัญ? ชั้นควรเป็นเจ้าของการพึ่งพาหรือเพียงแค่สังเกตพวกเขา?


1
ตั้งแต่เริ่มต้นถ้าคุณสามารถคอมไพล์ภายใต้ C ++ 11 และ / หรือไม่เคยการใช้ตัวชี้ธรรมดาเป็นวิธีจัดการทรัพยากรในคลาสไม่ใช่วิธีที่แนะนำอีกต่อไป ถ้าคลาส Foobar เป็นเจ้าของทรัพยากรเพียงคนเดียวและทรัพยากรควรเป็นอิสระเมื่อ Foobar พ้นขอบเขตstd::unique_ptrเป็นหนทางที่จะไป คุณสามารถใช้std::move()เพื่อโอนการเป็นเจ้าของทรัพยากรจากขอบเขตบนไปยังคลาส
Andy

1
ฉันจะรู้ได้อย่างไรว่าFoobarเป็นเจ้าของคนเดียว? ในกรณีเก่ามันง่าย แต่ปัญหากับ DI อย่างที่ฉันเห็นก็คือมันแยกชั้นจากการสร้างการพึ่งพามันก็แยกตัวมันออกจากการเป็นเจ้าของการพึ่งพาเหล่านั้นด้วย ในสภาพแวดล้อมที่เก็บขยะเช่น Java นั่นไม่ใช่ปัญหา ใน C ++ นี่คือ
el.pescado

1
@ el.pescado นอกจากนี้ยังมีปัญหาเดียวกันใน Java หน่วยความจำไม่ได้เป็นเพียงทรัพยากร นอกจากนี้ยังมีไฟล์และการเชื่อมต่อเช่น วิธีปกติในการแก้ปัญหานี้ใน Java คือการมีคอนเทนเนอร์ที่จัดการวงจรชีวิตของวัตถุที่มีอยู่ทั้งหมด ดังนั้นคุณไม่ต้องกังวลกับมันในวัตถุที่อยู่ในภาชนะ DI สิ่งนี้สามารถใช้งานกับแอปพลิเคชัน c ++ ได้หากมีวิธีที่ DI คอนเทนเนอร์จะรู้ว่าเมื่อใดควรสลับไปที่ช่วงวัฏจักรชีวิตใด
SpaceTrucker

3
แน่นอนคุณสามารถทำได้ด้วยวิธีที่ล้าสมัยโดยไม่มีความซับซ้อนทั้งหมดและมีรหัสที่ใช้งานได้และง่ายต่อการบำรุงรักษา (โปรดทราบว่าเด็กชาย Java อาจหัวเราะ แต่พวกเขามักจะดำเนินการเกี่ยวกับการปรับโครงสร้างใหม่ดังนั้นการแยกกันไม่ได้ช่วยพวกเขามากนัก)
gbjbaanb

3
การมีคนอื่นหัวเราะเยาะคุณไม่ใช่เหตุผลของการเป็นเหมือนพวกเขา ฟังข้อโต้แย้งของพวกเขา (นอกเหนือจาก "เพราะเราถูกบอกให้") และดำเนินการ DI หากมันนำมาซึ่งผลประโยชน์ที่เป็นรูปธรรมให้กับโครงการของคุณ ในกรณีนี้ให้คิดว่ารูปแบบเป็นกฎทั่วไปมากซึ่งคุณต้องใช้ในแบบที่เฉพาะเจาะจงกับโครงการ - ทั้งสามแนวทาง (และแนวทางที่เป็นไปได้อื่น ๆ ทั้งหมดที่คุณยังไม่ได้คิด) นั้นถูกต้อง พวกเขาก่อให้เกิดประโยชน์โดยรวม (เช่นมี Upside มากกว่าข้อเสีย)

คำตอบ:


2

ฉันตั้งใจจะเขียนสิ่งนี้เป็นความคิดเห็น แต่มันกลับกลายเป็นว่านานเกินไป

ฉันจะรู้ได้อย่างไรว่าFoobarเป็นเจ้าของคนเดียว? ในกรณีเก่ามันง่าย แต่ปัญหากับ DI อย่างที่ฉันเห็นก็คือมันแยกชั้นจากการสร้างการพึ่งพามันก็แยกตัวมันออกจากการเป็นเจ้าของการพึ่งพาเหล่านั้น ในสภาพแวดล้อมที่เก็บขยะเช่น Java นั่นไม่ใช่ปัญหา ใน C ++ นี่คือ

ไม่ว่าคุณจะใช้std::unique_ptr<Widget>หรือstd::shared_ptr<Widget>ว่ามันก็ขึ้นอยู่กับคุณในการตัดสินใจและมาจากการทำงานของคุณ

สมมติว่าคุณมีซึ่งเป็นผู้รับผิดชอบสำหรับการสร้างบล็อกของคุณเช่นUtilities::Factory Foobarการปฏิบัติตามหลักการ DI คุณจะต้องใช้Widgetอินสแตนซ์ในการฉีดโดยใช้FoobarConstructor ซึ่งหมายถึงภายในหนึ่งในUtilities::Factoryวิธีการตัวอย่างเช่นcreateWidget(const std::vector<std::string>& params)คุณสร้าง Widget และฉีดเข้าไปในFoobarวัตถุ

ตอนนี้คุณมีUtilities::Factoryวิธีการซึ่งสร้างWidgetวัตถุ หมายความว่าฉันควรรับผิดชอบวิธีการลบหรือไม่ ไม่แน่นอน มีเพียงการทำให้คุณเป็นตัวอย่าง


สมมติว่าคุณกำลังพัฒนาแอปพลิเคชันซึ่งจะมีหลายหน้าต่าง แต่ละหน้าต่างจะแสดงโดยใช้Foobarคลาสดังนั้นในความเป็นจริงFoobarกระทำเช่นตัวควบคุม

ตัวควบคุมอาจใช้ประโยชน์จากบางส่วนของคุณWidgetและคุณต้องถามตัวเอง:

ถ้าฉันไปที่หน้าต่างเฉพาะในใบสมัครของฉันฉันจะต้องใช้วิดเจ็ตเหล่านี้ วิดเจ็ตเหล่านี้ถูกแบ่งใช้ระหว่างหน้าต่างแอ็พพลิเคชันอื่นหรือไม่? ถ้าเป็นเช่นนั้นฉันไม่ควรสร้างพวกเขาซ้ำอีกเพราะพวกเขาจะดูเหมือนกันเสมอเพราะพวกเขาแบ่งปัน

std::shared_ptr<Widget> เป็นวิธีที่จะไป

นอกจากนี้คุณยังมีหน้าต่างแอปพลิเคชันซึ่งมีการWidgetเชื่อมโยงกับหน้าต่างนี้เท่านั้นโดยเฉพาะหมายความว่าหน้าต่างจะไม่ปรากฏที่ใดก็ได้ ดังนั้นหากคุณปิดหน้าต่างคุณไม่ต้องการWidgetแอปพลิเคชันของคุณอีกต่อไปหรืออย่างน้อยก็อินสแตนซ์ของมัน

นั่นคือที่std::unique_ptr<Widget>มาเพื่อเรียกร้องบัลลังก์


ปรับปรุง:

ฉันไม่เห็นด้วยกับ@DominicMcDonnellเกี่ยวกับปัญหาตลอดชีวิต การเรียกstd::moveใช้การstd::unique_ptrโอนการเป็นเจ้าของอย่างสมบูรณ์ดังนั้นแม้ว่าคุณจะสร้างobject Aวิธีการหนึ่งและส่งต่อไปยังผู้อื่นobject Bซึ่งเป็นที่พึ่งobject Bจะต้องรับผิดชอบทรัพยากรของobject Aและจะลบออกอย่างถูกต้องเมื่อobject Bออกนอกขอบเขต


แนวคิดทั่วไปของการเป็นเจ้าของและตัวชี้อัจฉริยะนั้นชัดเจนสำหรับฉัน ดูเหมือนว่าฉันจะไม่เล่นดีกับความคิดของ DI สำหรับฉันแล้ว Dependency Injection นั้นเกี่ยวกับคลาส decoupling จากการพึ่งพาของมัน แต่ในกรณีนี้คลาสยังคงมีผลต่อการสร้างการพึ่งพา
el.pescado

ตัวอย่างเช่นปัญหาที่ฉันมีunique_ptrคือการทดสอบหน่วย (DI ถูกโฆษณาว่าเป็นมิตรกับการทดสอบ): ฉันต้องการทดสอบFoobarดังนั้นฉันจึงสร้างWidgetส่งต่อไปยังFoobarการออกกำลังกายFoobarและจากนั้นฉันต้องการตรวจสอบWidgetแต่ถ้าFoobarอย่างใดเปิดเผยฉันสามารถ 'T Foobarเนื่องจากมันได้รับการโดยอ้างว่า
el.pescado

@ el.pescado คุณกำลังผสมสองสิ่งเข้าด้วยกัน คุณกำลังผสมการเข้าถึงและความเป็นเจ้าของ เพียงเพราะFoobarเจ้าของทรัพยากรไม่ได้หมายความว่าไม่มีใครควรใช้มัน คุณสามารถใช้Foobarวิธีการเช่นWidget* getWidget() const { return this->_widget.get(); }ซึ่งจะส่งคืนตัวชี้ raw ที่คุณสามารถทำงานได้ จากนั้นคุณสามารถใช้วิธีนี้เป็นอินพุตสำหรับการทดสอบหน่วยของคุณเมื่อคุณต้องการทดสอบWidgetคลาส
แอนดี้

จุดที่ดีที่ทำให้รู้สึก
el.pescado

1
หากคุณแปลง shared_ptr เป็น unique_ptr คุณจะรับประกันความเป็นเอกลักษณ์อย่างไร
Aconcagua

2

ฉันจะใช้ตัวชี้การสังเกตในรูปแบบของการอ้างอิง มันให้ไวยากรณ์ที่ดีขึ้นมากเมื่อคุณใช้และมีประโยชน์ทางความหมายซึ่งไม่ได้หมายความถึงความเป็นเจ้าของซึ่งตัวชี้ธรรมดาสามารถ

ปัญหาที่ใหญ่ที่สุดด้วยวิธีนี้คืออายุการใช้งาน คุณต้องให้แน่ใจว่าการพึ่งพาถูกสร้างขึ้นมาก่อนและถูกทำลายหลังจากชั้นเรียนของคุณ มันไม่ใช่ปัญหาง่าย การใช้ตัวชี้ที่ใช้ร่วมกัน (เป็นที่เก็บข้อมูลอ้างอิงและในชั้นเรียนทั้งหมดที่ขึ้นอยู่กับตัวเลือก 2 ด้านบน) สามารถลบปัญหานี้ได้ แต่ยังแนะนำปัญหาของการพึ่งพาแบบวงกลมซึ่งไม่น่ารำคาญและในความคิดของฉันไม่ค่อยชัดเจน ตรวจสอบก่อนที่จะทำให้เกิดปัญหา นี่คือเหตุผลที่ฉันไม่ต้องการทำมันโดยอัตโนมัติและจัดการอายุการใช้งานและคำสั่งการก่อสร้างด้วยตนเอง ฉันเคยเห็นระบบที่ใช้วิธีเทมเพลตแบบแสงซึ่งสร้างรายการของวัตถุตามลำดับที่พวกเขาสร้างขึ้นและทำลายพวกเขาในลำดับที่ตรงกันข้ามมันไม่สามารถจะเข้าใจผิด แต่มันทำให้สิ่งต่าง ๆ ง่ายขึ้นมาก

ปรับปรุง

คำตอบของ David Packer ทำให้ฉันคิดถึงคำถามอีกเล็กน้อย คำตอบเดิมนั้นเป็นจริงสำหรับการอ้างอิงที่ใช้ร่วมกันในประสบการณ์ของฉันซึ่งเป็นหนึ่งในข้อดีของการฉีดการพึ่งพาคุณสามารถมีหลายอินสแตนซ์โดยใช้อินสแตนซ์หนึ่งของการอ้างอิง ถ้าอย่างไรก็ตามคลาสของคุณจำเป็นต้องมีเป็นอินสแตนซ์ของการพึ่งพาเฉพาะนั้นstd::unique_ptrเป็นคำตอบที่ถูกต้อง


1

ก่อนอื่น - นี่คือ C ++ ไม่ใช่ Java - และที่นี่มีหลายสิ่งที่แตกต่างกัน คน Java ไม่มีปัญหาเหล่านั้นเกี่ยวกับการเป็นเจ้าของเนื่องจากมีการรวบรวมขยะอัตโนมัติที่แก้ปัญหาเหล่านั้นได้

ประการที่สอง: ไม่มีคำตอบทั่วไปสำหรับคำถามนี้ - มันขึ้นอยู่กับข้อกำหนดคืออะไร!

ปัญหาเกี่ยวกับการมีเพศสัมพันธ์ FooBar และ Widget คืออะไร? FooBar ต้องการใช้วิดเจ็ตและหากทุกอินสแตนซ์ของ FooBar จะมีวิดเจ็ตของตัวเองและเป็นเครื่องมือตัวเดียวกันอยู่แล้วปล่อยให้มันเป็นคู่ ...

ใน C ++ คุณสามารถทำสิ่งที่ "แปลก" ที่ไม่มีอยู่ใน Java เช่นมีตัวสร้างเทมเพลต variadic (ดีมี ... สัญลักษณ์ใน Java ซึ่งสามารถใช้ในการก่อสร้างเกินไป แต่นั่นคือ เพียงแค่น้ำตาลประโยคเพื่อซ่อนอาเรย์วัตถุซึ่งอันที่จริงไม่มีส่วนเกี่ยวข้องกับเทมเพลต Variadic จริง ๆ !) - หมวดหมู่ 'เมตาแมปการเขียนโปรแกรมบางอย่างที่บ้าคลั่ง':

namespace WidgetFactory
{
    Widget* create(int, int)
    {
        return 0;
    }
    Widget* create(int, bool, long)
    {
        return 0;
    }
}
class FooBar
{
public:
    template < typename ...Arguments >
    FooBar(Arguments... arguments)
        : mWidget(WidgetFactory::create(arguments...))
    {
    }
    ~FooBar()
    {
        delete mWidget;
    }
private:
    Widget* mWidget;
};

FooBar foobar1(10, 12);
FooBar foobar2(51, true, 54L);

ชอบมัน?

แน่นอนว่ายังมีเหตุผลเมื่อคุณต้องการหรือจำเป็นที่จะต้องแยกทั้งเรียน - เช่นถ้าคุณจำเป็นต้องสร้างเครื่องมือในเวลาที่ห่างไกลก่อนเช่น FooBar ใด ๆ ที่มีอยู่ถ้าคุณต้องการหรือจำเป็นต้องนำมาใช้ใหม่เครื่องมือ, ... , หรือเพียงแค่ เพราะสำหรับปัญหาปัจจุบันมันเหมาะสมกว่า (เช่นถ้า Widget เป็นองค์ประกอบ GUI และ FooBar จะ / สามารถ / ต้องไม่เป็นหนึ่ง)

จากนั้นเรากลับไปที่จุดที่สอง: ไม่มีคำตอบทั่วไป คุณต้องตัดสินใจว่าอะไร - สำหรับปัญหาจริง - เป็นทางออกที่เหมาะสมกว่า ฉันชอบวิธีการอ้างอิงของ DominicMcDonnell แต่สามารถใช้ได้หาก FooBar นั้นไม่สามารถถือกรรมสิทธิ์ได้ (จริง ๆ แล้วคุณทำได้ แต่นั่นก็หมายถึงรหัสที่สกปรกมาก ... ) นอกจากนั้นฉันเข้าร่วมคำตอบของ David Packer (อันที่ควรเขียนเป็นความคิดเห็น แต่เป็นคำตอบที่ดี)


1
ใน C ++ อย่าใช้classes โดยไม่มีอะไรเลยนอกจากวิธีการคงที่แม้ว่ามันจะเป็นเพียงแค่โรงงานในตัวอย่างของคุณ นั่นเป็นการละเมิดหลักการ OO ใช้เนมสเปซแทนและจัดกลุ่มฟังก์ชันของคุณโดยใช้
Andy

@DavidPacker อืมแน่นอนว่าคุณพูดถูก - ฉันคิดเงียบ ๆ ว่าคลาสจะมี internals ส่วนตัว แต่ไม่สามารถมองเห็นหรือพูดถึงที่อื่น ...
Aconcagua

1

คุณไม่มีตัวเลือกเพิ่มเติมอย่างน้อยสองตัวเลือกที่พร้อมใช้งานใน C ++:

หนึ่งคือการใช้การฉีดพึ่งพา 'คงที่' ที่การอ้างอิงของคุณเป็นพารามิเตอร์แม่แบบ สิ่งนี้จะช่วยให้คุณมีทางเลือกในการเก็บการพึ่งพาของคุณตามค่าในขณะที่ยังยอมให้มีการรวบรวมการพึ่งพาเวลา คอนเทนเนอร์ STL ใช้วิธีนี้สำหรับตัวจัดสรรและการเปรียบเทียบและฟังก์ชันแฮช

อีกประการหนึ่งคือการใช้วัตถุ polymorphic ตามค่าโดยใช้สำเนาลึก วิธีดั้งเดิมในการทำเช่นนี้คือการใช้วิธีการโคลนเสมือนจริงอีกทางเลือกหนึ่งที่ได้รับความนิยมคือการใช้การลบประเภทเพื่อสร้างประเภทค่าที่มีความหลากหลาย

ตัวเลือกใดที่เหมาะสมที่สุดขึ้นอยู่กับกรณีการใช้งานของคุณมันยากที่จะให้คำตอบทั่วไป หากคุณต้องการความหลากหลายรูปแบบคงที่แม้ว่าฉันจะบอกว่าแม่แบบเป็นวิธี C ++ ที่สุดที่จะไป


0

คุณไม่สนใจคำตอบที่เป็นไปได้ข้อที่สี่ซึ่งรวมรหัสแรกที่คุณโพสต์ ("วันเฒ่าที่ดี" ในการจัดเก็บสมาชิกตามค่า) ด้วยการฉีดขึ้นต่อกัน:

class Foobar {
    Widget widget;
public:
    Foobar(Widget w) // pass w by value
     : widget{ std::move(w) } {}
};

รหัสลูกค้าสามารถเขียนได้:

Widget w;
Foobar f1{ w }; // default: copy w into f1
Foobar f2{ std::move(w) }; // move w into f2

การเป็นตัวแทนของการมีเพศสัมพันธ์ระหว่างวัตถุไม่ควรทำ (หมดจด) ในเกณฑ์ที่คุณระบุไว้ (นั่นคือไม่ได้ขึ้นอยู่กับ "ซึ่งจะดีกว่าสำหรับการจัดการอายุการใช้งานที่ปลอดภัย")

คุณยังสามารถใช้เกณฑ์เชิงแนวคิด ("รถมีสี่ล้อ" กับ "รถใช้สี่ล้อซึ่งคนขับต้องนำมาด้วย")

คุณสามารถมีเกณฑ์ที่กำหนดโดย API อื่น ๆ (หากสิ่งที่คุณได้รับจาก API เป็น wrapper ที่กำหนดเองหรือ std :: unique_ptr เช่นตัวเลือกในรหัสลูกค้าของคุณจะถูก จำกัด เช่นกัน)


1
สิ่งนี้ขัดขวางพฤติกรรม polymorphic ซึ่ง จำกัด มูลค่าเพิ่มอย่างรุนแรง
el.pescado

จริง ฉันแค่คิดว่าจะพูดถึงมันเพราะมันเป็นรูปแบบที่ฉันใช้มากที่สุด (ฉันใช้การสืบทอดในการเขียนโค้ดของฉันสำหรับพฤติกรรม polymorphic - ไม่ใช่การใช้รหัสซ้ำดังนั้นฉันจึงไม่มีหลายกรณีที่ต้องการพฤติกรรม polymorphic) ..
utnapistim
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.