การทำให้คอนสตรัคเตอร์เป็นส่วนตัวในคลาสคืออะไร?


135

เหตุใดเราจึงควรทำให้ตัวสร้างเป็นส่วนตัวในชั้นเรียน เนื่องจากเราต้องการให้ผู้สร้างเป็นสาธารณะเสมอ

คำตอบ:


130

เหตุผลบางประการที่คุณอาจต้องการตัวสร้างส่วนตัว:

  1. ตัวสร้างสามารถเข้าถึงได้จากวิธีการโรงงานแบบคงที่ภายในคลาสเท่านั้น Singleton สามารถอยู่ในหมวดหมู่นี้ได้เช่นกัน
  2. คลาสยูทิลิตี้ที่มีเฉพาะเมธอดแบบคงที่

10
ฉันจะไม่ต้องกังวลกับการสร้างคอนสตรัคเตอร์ส่วนตัวสำหรับคลาสยูทิลิตี้
Petesh

16
@Patesh: นั่นคือการตัดสินใจของคุณ อื่น ๆ และฉันค่อนข้างจะป้องกันการสร้างอินสแตนซ์ของคลาสยูทิลิตี้มากกว่าที่จะปล่อยตัวสร้างส่วนตัวหนึ่งบรรทัด
nanda

1
ในภาษาโปรแกรมบางภาษา (โดยเฉพาะ Java) ยังป้องกันการสืบทอด
dfa

9
@dfa: เพื่อป้องกันการสืบทอดคุณต้องจัดfinalระดับชั้นเรียน การใส่คอนสตรัคเตอร์ส่วนตัวค่อนข้างไร้ประโยชน์ด้วยเหตุผลนี้
nanda

3
@ Will: ไม่ใช่ถ้าคุณใช้การสะท้อนแสง การประกาศคอนสตรัคเตอร์แบบส่วนตัวมีวัตถุประสงค์เพื่อป้องกันการสร้างอินสแตนซ์ (จำกัด การสะท้อนกลับ) แต่การป้องกันคลาสย่อยเป็นผลข้างเคียงไม่ใช่เจตนา finalเครื่องมือที่เหมาะสมสำหรับการนี้จะประกาศในชั้นเรียน นี่คือสิ่งที่ Sun ทำกับคลาส String และส่วนที่สมเหตุสมผลของ API ที่ไม่ควรขยาย
BobMcGee

97

การจัดเตรียมคอนสตรัคเตอร์ส่วนตัวคุณจะป้องกันไม่ให้สร้างอินสแตนซ์คลาสในที่อื่นนอกเหนือจากคลาสนี้ มีหลายกรณีการใช้งานสำหรับการจัดหาตัวสร้างดังกล่าว

A. อินสแตนซ์คลาสของคุณถูกสร้างขึ้นด้วยstaticวิธีการ วิธีการประกาศแล้วstaticpublic

class MyClass()
{
private:
  MyClass() { }

public:
  static MyClass * CreateInstance() { return new MyClass(); }
};

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

class MyClass()
{
private:
  MyClass() { }

public:
  MyClass & Instance()
  {
    static MyClass * aGlobalInst = new MyClass();
    return *aGlobalInst;
  }
};

C. (ใช้กับมาตรฐาน C ++ 0x ที่กำลังจะมาถึงเท่านั้น) คุณมีตัวสร้างหลายตัว บางคนก็มีการประกาศอื่น ๆpublic privateสำหรับการลดขนาดรหัสผู้สร้างสาธารณะ 'เรียก' ตัวสร้างส่วนตัวซึ่งจะทำงานทั้งหมด ผู้publicสร้างของคุณจึงเรียกว่าผู้สร้างมอบหมาย :

class MyClass
{
public:
  MyClass() : MyClass(2010, 1, 1) { }

private:
  MyClass(int theYear, int theMonth, int theDay) { /* do real work */ }
};

D. คุณต้องการ จำกัด การคัดลอกออบเจ็กต์ (ตัวอย่างเช่นเนื่องจากการใช้ทรัพยากรที่ใช้ร่วมกัน):

class MyClass
{
  SharedResource * myResource;

private:
  MyClass(const MyClass & theOriginal) { }
};

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


3
ในกรณีส่วนใหญ่คุณต้องการป้องกันการคัดลอกวัตถุ ดังนั้นคุณจะไม่จัดเตรียมการใช้งานสำหรับตัวสร้างสำเนาส่วนตัว
ศุกร์ที่

จากคู่มือสไตล์ google c ++ (อีกตัวอย่างหนึ่งที่ไม่อยู่ในรายการ แต่เป็นเรื่องธรรมดา) -> หากคุณต้องการทำงานในตัวสร้าง "พิจารณาฟังก์ชันจากโรงงาน [และทำให้ตัวสร้างเป็นแบบส่วนตัว]" (ฉันเขียนข้อความในวงเล็บเหลี่ยม )
Trevor Boyd Smith

12

ในการออกจาก "ประตูหลัง" ที่อนุญาตให้เพื่อนชั้นเรียน / ฟังก์ชันอื่นสร้างวัตถุในลักษณะที่ห้ามไม่ให้ผู้ใช้ ตัวอย่างที่อยู่ในใจคือคอนเทนเนอร์ที่สร้างตัววนซ้ำ (C ++):

Iterator Container::begin() { return Iterator(this->beginPtr_); }
// Iterator(pointer_type p) constructor is private,
//     and Container is a friend of Iterator.

เทอร์รี่แตกต่างกันพอไหม ;)
Emile Cormier

คำตอบที่ดี. ทำไมไม่พูดถึงการไตร่ตรองเนื่องจากนี่เป็นหนทางหนึ่งในการบรรลุสิ่งที่ผู้ใช้ไม่สามารถทำได้
BobMcGee

@ บ็อบ: คุณจะต้องสอนฉันเกี่ยวกับเรื่องนั้นเนื่องจากฉันเป็นคน C ++ เป็นหลักและการไตร่ตรองไม่ได้อยู่ในคำศัพท์ C ++ จริงๆ ;)
Emile Cormier

3
ไม่มีใครจะอ่านสิ่งนี้ แต่ต่อไปนี้: ฉันได้รับการโหวตลงคะแนนสองสามครั้งเกี่ยวกับเรื่องนี้ ไม่ได้อารมณ์เสียหรืออะไร แต่ (เพื่อประโยชน์ในการเรียนรู้) ฉันอยากรู้ว่าทำไมถึงแย่? ตัววนซ้ำสามารถสร้างด้วยข้อมูลที่จำเป็นในการเข้าถึงข้อมูลของคอนเทนเนอร์ได้อย่างไร
Emile Cormier

2
@EmileCormier ฉันคิดว่าคุณได้รับการโหวตลดลงอย่างไม่เป็นธรรมเพราะคนที่เรียนภาษา C ++ ถูกบอกครั้งแล้วครั้งเล่า: "หลีกเลี่ยงการประกาศว่าเป็นเพื่อน" คำแนะนำนี้ดูเหมือนจะมุ่งเป้าไปที่โปรแกรมเมอร์ C ++ ที่ไม่มีประสบการณ์ซึ่งอาจใช้ในfriendที่ที่ไม่ได้รับการรับประกันและมีหลายกรณีที่เป็นความคิดที่ไม่ดี น่าเศร้าที่ข้อความที่ได้รับการตอบรับอย่างดีเกินไปและนักพัฒนาหลายคนไม่เคยเรียนรู้ภาษาดีพอที่จะเข้าใจว่าการใช้งานเป็นครั้งคราวของfriendเป็นที่ยอมรับไม่เพียง แต่ที่นิยม ตัวอย่างของคุณเป็นเพียงกรณีนี้ การมีเพศสัมพันธ์ที่แข็งแกร่งโดยเจตนาไม่ใช่อาชญากรรม แต่เป็นการตัดสินใจในการออกแบบ
evadeflow

10

ทุกคนติดอยู่กับสิ่ง Singleton ว้าว

สิ่งอื่น ๆ:

  • หยุดไม่ให้ผู้อื่นสร้างชั้นเรียนของคุณในสแต็ก สร้างช่างก่อสร้างส่วนตัวและตัวชี้หลังด้วยวิธีโรงงานเท่านั้น
  • การป้องกันการสร้างสำเนาของคลาส (ตัวสร้างสำเนาส่วนตัว)

8

สิ่งนี้มีประโยชน์มากสำหรับตัวสร้างที่มีรหัสทั่วไป ผู้สร้างส่วนตัวสามารถเรียกได้โดยผู้สร้างรายอื่นโดยใช้ 'this (... );' สัญกรณ์ ด้วยการสร้างรหัสเริ่มต้นทั่วไปในตัวสร้างส่วนตัว (หรือที่มีการป้องกัน) คุณยังทำให้ชัดเจนว่าจะถูกเรียกในระหว่างการก่อสร้างเท่านั้นซึ่งจะไม่เป็นเช่นนั้นหากเป็นเพียงวิธีการ:

public class Point {
   public Point() {
     this(0,0); // call common constructor
   }
   private Point(int x,int y) {
     m_x = x; m_y = y;
   }
};

3

มีบางกรณีที่คุณอาจไม่ต้องการใช้ตัวสร้างสาธารณะ ตัวอย่างเช่นหากคุณต้องการคลาสซิงเกิลตัน

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


3

เพื่อให้แน่ใจว่าคุณ (คลาสที่มีคอนสตรัคเตอร์ส่วนตัว) ควบคุมวิธีการเรียกใช้คอนสตรัคเตอร์

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


@Skilldrick: ตัวอย่างหนึ่งคือคุณสามารถบังคับให้คลาสได้รับการจัดสรรฮีปเท่านั้น
Dirk

@dirk ฉันได้ลบความคิดเห็นของฉันแล้วเพราะมันไม่เกี่ยวข้องอีกต่อไป
Skilldrick

3

นอกจากนี้เรายังสามารถมีคอนสตรัคเตอร์ส่วนตัวเพื่อให้ออบเจ็กต์สร้างขึ้นโดยคลาสเฉพาะเท่านั้น (เพื่อเหตุผลด้านความปลอดภัย)

วิธีหนึ่งที่ทำได้คือการมีเพื่อนร่วมชั้นเรียน

ตัวอย่าง C ++:

class ClientClass;
class SecureClass 
{
  private:
    SecureClass();   // Constructor is private.
    friend class ClientClass;  // All methods in 
                               //ClientClass have access to private
                               // &   protected methods of SecureClass.
};

class ClientClass
{
public:
    ClientClass();
    SecureClass* CreateSecureClass()
     { 
           return (new SecureClass());  // we can access 
                                        // constructor of 
                                        // SecureClass as 
                                        // ClientClass is friend 
                                        // of SecureClass.
     }
};

หมายเหตุ: หมายเหตุ: เฉพาะ ClientClass (เนื่องจากเป็นเพื่อนกับ SecureClass) เท่านั้นที่สามารถเรียกใช้ตัวสร้างของ SecureClass ได้


2

2

เมื่อคุณไม่ต้องการให้ผู้ใช้สร้างอินสแตนซ์ของคลาสนี้หรือสร้างคลาสที่สืบทอดคลาสนี้เช่นjava.lang.mathฟังก์ชันทั้งหมดในแพ็กเกจนี้คือstaticฟังก์ชันทั้งหมดสามารถเรียกใช้ได้โดยไม่ต้องสร้างอินสแตนซ์mathดังนั้นคอนสตรัคเตอร์จึงประกาศเป็นแบบคงที่ .


1

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

มีการอภิปรายและบางตัวอย่างเพิ่มเติมเป็นที่นี่


1

ฉันเห็นคำถามจากคุณในประเด็นเดียวกันนี้

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


1

คุณไม่ควรกำหนดให้ตัวสร้างเป็นแบบส่วนตัว ระยะเวลา ทำให้ได้รับการป้องกันเพื่อให้คุณสามารถขยายชั้นเรียนได้หากต้องการ

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

หากเป็นคลาสยูทิลิตี้วิธีแก้ปัญหาที่ง่ายกว่าถูกต้องและสวยงามกว่าคือการทำเครื่องหมายทั้งคลาสเป็น "static final" เพื่อป้องกันการขยาย การทำเครื่องหมายคอนสตรัคเตอร์เป็นส่วนตัวไม่ได้ผลดี ผู้ใช้ที่ตั้งใจจริงอาจใช้การสะท้อนเพื่อให้ได้ตัวสร้าง

การใช้งานที่ถูกต้อง:

  • การใช้ a protected สร้างคือการบังคับใช้วิธีการโรงงานแบบคงที่ซึ่งช่วยให้คุณสามารถ จำกัด การสร้างอินสแตนซ์หรือพูลและนำทรัพยากรที่มีราคาแพงกลับมาใช้ใหม่ (การเชื่อมต่อ DB ทรัพยากรดั้งเดิม)
  • Singletons (โดยปกติไม่ใช่แนวทางปฏิบัติที่ดี แต่บางครั้งก็จำเป็น)

2
จากประสบการณ์ของฉันไม่มีความจริงที่แน่นอนแม้แต่ goto ก็อาจถูกใช้หากสถานการณ์เรียกร้อง มีวิธีที่ไม่ดีและชั่วร้าย แต่อย่างที่ Marshall Cline วางไว้บางครั้งคุณต้องเลือกระหว่างความชั่วร้ายที่น้อยกว่า parashift.com/c++-faq-lite/big-picture.html#faq-6.15 สำหรับช่างก่อสร้างส่วนตัวพวกเขาจำเป็นและไม่ได้เลวร้ายสำหรับคุณ หมายความว่าไม่มีใครรวมถึงคลาสย่อยของคุณที่ควรใช้
ดารามารักษ์

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

การคัดลอกโครงสร้างหรือค่าเริ่มต้นสร้างคลาสบางคลาสไม่สมเหตุสมผล ใน C ++ คุณระบุสิ่งนี้โดยการประกาศ Private ctor โดยไม่ได้กำหนด (ซึ่งเป็นเรื่องปกติสำหรับ operator =)

การเพิ่มประสิทธิภาพคอมไพเลอร์เช่น Java JIT และ GWT ใช้ประโยชน์จากตัวสร้างส่วนตัวเพื่อ จำกัด ขอบเขตของการสร้างอินสแตนซ์และทำงานได้ดีขึ้นในการแทรก / ตัดโค้ดของคุณ
Ajax

1

Constructor เป็นแบบส่วนตัวสำหรับจุดประสงค์บางอย่างเช่นเมื่อคุณต้องการใช้ singleton หรือ จำกัด จำนวนอ็อบเจ็กต์ของคลาส ตัวอย่างเช่นในการใช้งานซิงเกิลตันเราต้องทำให้ตัวสร้างเป็นแบบส่วนตัว

#include<iostream>
using namespace std;
class singletonClass
{


    static int i;
    static singletonClass* instance;
public:


    static singletonClass* createInstance()
    {


        if(i==0)
        {

            instance =new singletonClass;
            i=1;

        }

        return instance;

    }
    void test()
    {

        cout<<"successfully created instance";
    }
};

int singletonClass::i=0;
singletonClass* singletonClass::instance=NULL;
int main()
{


    singletonClass *temp=singletonClass::createInstance();//////return instance!!!
    temp->test();
}

อีกครั้งหากคุณต้องการ จำกัด การสร้างวัตถุไม่เกิน 10 ให้ใช้สิ่งต่อไปนี้

#include<iostream>
using namespace std;
class singletonClass
{


    static int i;
    static singletonClass* instance;
public:


    static singletonClass* createInstance()
    {


        if(i<10)
        {

            instance =new singletonClass;
            i++;
            cout<<"created";

        }

        return instance;

    }
};

int singletonClass::i=0;
singletonClass* singletonClass::instance=NULL;
int main()
{


    singletonClass *temp=singletonClass::createInstance();//return an instance
    singletonClass *temp1=singletonClass::createInstance();///return another instance

}

ขอบคุณ


1

คุณสามารถมีตัวสร้างได้มากกว่าหนึ่งตัว C ++ มีตัวสร้างเริ่มต้นและตัวสร้างสำเนาเริ่มต้นหากคุณไม่ได้ระบุอย่างชัดเจน สมมติว่าคุณมีคลาสที่สามารถสร้างได้โดยใช้ตัวสร้างพารามิเตอร์บางตัวเท่านั้น บางทีอาจเป็นตัวแปรเริ่มต้น หากผู้ใช้ใช้คลาสนี้โดยไม่มีตัวสร้างนั้นก็สามารถทำให้เกิดปัญหาได้ไม่สิ้นสุด กฎทั่วไปที่ดี: หากการใช้งานเริ่มต้นไม่ถูกต้องให้กำหนดทั้งตัวสร้างเริ่มต้นและตัวคัดลอกเป็นแบบส่วนตัวและอย่าให้การใช้งาน

class C
{
public:
    C(int x);

private:
    C();
    C(const C &);
};

ใช้คอมไพลเลอร์เพื่อป้องกันไม่ให้ผู้ใช้ใช้อ็อบเจ็กต์กับตัวสร้างดีฟอลต์ที่ไม่ถูกต้อง


2
เมื่อคุณจัดเตรียมคอนสตรัคเตอร์ที่ไม่ใช่ดีฟอลต์โดยไม่ระบุคอนสตรัคเตอร์ดีฟอลต์ตัวสร้างดีฟอลต์จะไม่มีอยู่ ไม่จำเป็นต้องสร้างและทำให้เป็นส่วนตัว
TT_

0

การอ้างอิงจากJava ที่มีประสิทธิภาพคุณสามารถมีคลาสที่มีตัวสร้างส่วนตัวเพื่อให้มีคลาสยูทิลิตี้ที่กำหนดค่าคงที่ (เป็นฟิลด์สุดท้ายแบบคงที่)

( แก้ไข:ตามความคิดเห็นนี่เป็นสิ่งที่อาจใช้ได้กับ Java เท่านั้นฉันไม่ทราบว่าโครงสร้างนี้ใช้ได้ / จำเป็นในภาษา OO อื่น ๆ หรือไม่ (พูด C ++))

ตัวอย่างด้านล่าง:

public class Constants {
    private Contants():

    public static final int ADDRESS_UNIT = 32;
    ...
}

EDIT_1 : อีกครั้งคำอธิบายด้านล่างใช้ได้กับ Java: (และอ้างอิงจากหนังสือEffective Java )

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

ตัวอย่างเช่นสมมติว่าไม่มีตัวสร้างส่วนตัวสำหรับค่าคงที่ของคลาส โค้ดด้านล่างนี้ใช้ได้ แต่ไม่ได้สื่อถึงเจตนาของผู้ใช้คลาส Constants ได้ดีขึ้น

unit = (this.length)/new Constants().ADDRESS_UNIT;

ตรงกันข้ามกับรหัสเช่น

unit = (this.length)/Constants.ADDRESS_UNIT;

ฉันคิดว่าคอนสตรัคเตอร์ส่วนตัวบ่งบอกถึงความตั้งใจของผู้ออกแบบ คลาสConstants (พูด) ได้ดีกว่า

Java จัดเตรียมคอนสตรัคเตอร์สาธารณะที่ไม่มีพารามิเตอร์ดีฟอลต์หากไม่มีการจัดเตรียมคอนสตรัคเตอร์และหากคุณตั้งใจที่จะป้องกันการสร้างอินสแตนซ์จำเป็นต้องใช้คอนสตรัคเตอร์ส่วนตัว

เราไม่สามารถทำเครื่องหมายสแตติกคลาสระดับบนสุดได้และแม้แต่คลาสสุดท้ายก็สามารถสร้างอินสแตนซ์ได้


2
แต่ใน C ++ คุณไม่จำเป็นต้องมีคลาสสำหรับสิ่งนี้ หากบางสิ่งไม่ขึ้นอยู่กับข้อมูลภายในออบเจ็กต์ให้เขียนเป็นฟังก์ชันอิสระและตัวแปรอิสระ ต้องการการห่อหุ้ม? ใช้เนมสเปซ
daramarak

@daramarak: ขอบคุณฉันไม่มีประสบการณ์กับ C ++ ฉันอัปเดตคำตอบเพื่อแสดงว่าสิ่งนี้ใช้ได้เฉพาะใน Java
sateesh

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

@BobMcGee ขอบคุณสำหรับความคิดเห็นของคุณ สิ่งนี้ทำให้ฉันคิด (และอ้างอิง) มากขึ้นเกี่ยวกับสิ่งที่ฉันโพสต์ ฉันได้แก้ไขคำตอบเพื่อเพิ่มเหตุผลเพิ่มเติมเกี่ยวกับประโยชน์ของตัวสร้างส่วนตัว
ผ้าปูโต๊ะ

0

ชั้นเรียนยูทิลิตี้อาจมีตัวสร้างส่วนตัว ผู้ใช้ชั้นเรียนไม่ควรสร้างอินสแตนซ์ของคลาสเหล่านี้:

public final class UtilityClass {
    private UtilityClass() {}

    public static utilityMethod1() {
        ...
    }
}

1
เหตุใดจึงมีความสำคัญหากคลาสยูทิลิตี้ถูกสร้างอินสแตนซ์ เพียงแค่สร้างวัตถุที่ไม่มีช่องและกินหน่วยความจำไม่กี่ไบต์
BobMcGee

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

@BobMcGee: เห็นได้ชัดว่าการออกแบบคลาสที่ใช้งานได้และการออกแบบคลาสให้คนอื่นใช้เป็นสิ่งที่แตกต่างกัน ผู้ที่ทำงานในการพัฒนา API (เช่น Sun people หรือ Google collection guy) จะฆ่าใครก็ตามที่พยายามบอกว่าตัวสร้างส่วนตัวในคลาสยูทิลิตี้นั้นไร้ประโยชน์
nanda

@gpampara: การประกาศคลาสสุดท้ายของคุณจะป้องกันไม่ให้คนคลาสย่อย นี่คือสิ่งที่ซันทำกับคลาส String @Nanda: คลาสยูทิลิตี้ไม่จำเป็นต้องมีตัวสร้างที่กำหนด หากคุณไม่ต้องการให้ขยายได้ให้ประกาศว่าไม่ใช้ "ขั้นสุดท้าย"
BobMcGee

1
@BobMcGee: คุณน่าจะชอบดูตัวอย่างจาก Sun จากนั้นตรวจสอบเรื่องนี้คอลเลกชันระดับยูทิลิตี้จากดวงอาทิตย์: docjar.com/html/api/java/util/Collections.java.html มันใช้คอนสตรัคเตอร์ส่วนตัว
nanda

0

คุณอาจต้องการป้องกันไม่ให้ชั้นเรียนสามารถสร้างอินสแตนซ์ได้อย่างอิสระ ดูรูปแบบการออกแบบซิงเกิลตันเป็นตัวอย่าง เพื่อรับประกันความเป็นเอกลักษณ์คุณไม่สามารถให้ใครสร้างอินสแตนซ์ได้ :-)


0

การใช้งานที่สำคัญอย่างหนึ่งคือในคลาส SingleTon

class Person
{
   private Person()
   {
      //Its private, Hense cannot be Instantiated
   }

   public static Person GetInstance()
   {
       //return new instance of Person
       // In here I will be able to access private constructor
   }
};

นอกจากนี้ยังเหมาะสมหากชั้นเรียนของคุณมีวิธีการคงที่เท่านั้น กล่าวคือไม่มีใครต้องการสร้างอินสแตนซ์ชั้นเรียนของคุณ


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

SingleTon ไม่ใช่การต่อต้านรูปแบบ อย่างไรก็ตามการใช้รูปแบบมากเกินไป (เพราะขาดความรู้ในการใช้งาน) นั้นไม่ดี ควรสังเกตว่าเป็นหนึ่งในรูปแบบ GOF
SysAdmin

0

เป็นเหตุผลหนึ่งที่ชัดเจนจริงๆ: คุณต้องการสร้างวัตถุ แต่ไม่สามารถทำได้จริง (ในแง่ของอินเทอร์เฟซ) ภายในตัวสร้าง

Factoryตัวอย่างค่อนข้างชัดเจนให้ฉันแสดงให้เห็นถึงNamed Constructorสำนวน

สมมติว่าฉันมีคลาสComplexที่สามารถแทนจำนวนเชิงซ้อนได้

class Complex { public: Complex(double,double); .... };

คำถามคือผู้สร้างคาดหวังส่วนจริงและส่วนจินตภาพหรือไม่หรือคาดว่าจะมีบรรทัดฐานและมุม (พิกัดเชิงขั้ว)

ฉันสามารถเปลี่ยนอินเทอร์เฟซเพื่อให้ง่ายขึ้น:

class Complex
{
public:
  static Complex Regular(double, double = 0.0f);
  static Complex Polar(double, double = 0.0f);
private:
  Complex(double, double);
}; // class Complex

สิ่งนี้เรียกว่าNamed Constructoridiom: คลาสสามารถสร้างได้ตั้งแต่เริ่มต้นโดยระบุอย่างชัดเจนว่าตัวสร้างใดที่เราต้องการใช้

เป็นกรณีพิเศษของการก่อสร้างหลายวิธี รูปแบบการออกแบบให้เป็นจำนวนที่ดีของวิธีการที่จะสร้างวัตถุ: Builder, Factory, Abstract Factory... และนวกรรมิกส่วนตัวจะให้แน่ใจว่าผู้ใช้จะถูก จำกัด อย่างถูกต้อง


0

นอกเหนือจากการใช้งานที่รู้จักกันดี ...

ในการใช้รูปแบบMethod Objectซึ่งฉันจะสรุปเป็น:

"ตัวสร้างส่วนตัววิธีการคงที่สาธารณะ"
"วัตถุสำหรับการใช้งานฟังก์ชันสำหรับอินเทอร์เฟซ"

หากคุณต้องการที่จะใช้ฟังก์ชั่นการใช้วัตถุและวัตถุไม่ได้ออกไปข้างนอกที่มีประโยชน์ของการทำคำนวณ one-off (โดยเรียกวิธี) แล้วคุณมีวัตถุใบปลิว คุณสามารถห่อหุ้มการสร้างอ็อบเจ็กต์และการเรียกเมธอดในวิธีการแบบคงที่เพื่อป้องกันการต่อต้านรูปแบบทั่วไปนี้:

z = new A(x,y).call();

…แทนที่ด้วยการเรียกฟังก์ชัน (namespaced):

z = A.f(x,y);

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

ตัวอย่างเช่นถ้าคุณต้องการที่จะเลิกการคำนวณข้ามวิธีfoo, barและzorkยกตัวอย่างให้กับรัฐหุ้นโดยไม่ต้องส่งผ่านค่าจำนวนมากในและออกจากฟังก์ชั่นที่คุณสามารถใช้มันเป็นดังนี้:

class A {
  public static Z f(x, y) {
    A a = new A(x, y);
    a.foo();
    a.bar();
    return a.zork();
  }

  private A(X x, Y y) { /* ... */ };
}

รูปแบบวัตถุวิธีนี้มีให้ในSmalltalk Best Practice Patterns , Kent Beck, หน้า 34–37 ซึ่งเป็นขั้นตอนสุดท้ายของรูปแบบการปรับโครงสร้างใหม่โดยสิ้นสุด:

  1. แทนที่เมธอดเดิมด้วยวิธีที่สร้างอินสแตนซ์ของคลาสใหม่โดยสร้างด้วยพารามิเตอร์และตัวรับของเมธอดเดิมและเรียกใช้ "คำนวณ"

สิ่งนี้แตกต่างอย่างมีนัยสำคัญจากตัวอย่างอื่น ๆ ที่นี่: คลาสเป็นแบบทันทีได้ (ไม่เหมือนคลาสยูทิลิตี้) แต่อินสแตนซ์เป็นแบบส่วนตัว (ไม่เหมือนกับวิธีการของโรงงานรวมถึง singletons เป็นต้น) และสามารถอยู่บนสแต็กได้เนื่องจากไม่เคยหนี

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


0

บางครั้งก็มีประโยชน์หากคุณต้องการควบคุมว่าจะสร้างอินสแตนซ์ของออบเจ็กต์อย่างไรและเมื่อไหร่ (และจำนวน)

อื่น ๆ ที่ใช้ในรูปแบบ:

Singleton pattern
Builder pattern

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

0

การใช้คอนสตรัคเตอร์ส่วนตัวอาจช่วยเพิ่มความสามารถในการอ่าน / บำรุงรักษาเมื่อเผชิญกับการออกแบบที่ขับเคลื่อนด้วยโดเมน จาก "Microsoft .NET - Architecing Applications for the Enterprise, 2nd Edition":

var request = new OrderRequest(1234);

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

var request = OrderRequest.CreateForCustomer(1234);

ที่ไหน

private OrderRequest() { ... }

public OrderRequest CreateForCustomer (int customerId)
{
    var request = new OrderRequest();
    ...
    return request;
}

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

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