เราอยู่ได้โดยปราศจากสิ่งก่อสร้าง?


25

สมมติว่าด้วยเหตุผลบางอย่างวัตถุทั้งหมดจะถูกสร้างขึ้นด้วยวิธีนี้ $ obj = CLASS :: getInstance () จากนั้นเราฉีดการอ้างอิงโดยใช้ setters และทำการเริ่มต้นการเริ่มต้นโดยใช้ $ obj-> initInstance (); มีปัญหาหรือสถานการณ์จริงใดบ้างที่ไม่สามารถแก้ไขได้ถ้าเราจะไม่ใช้ตัวสร้างเลย

Ps เหตุผลในการสร้างวัตถุด้วยวิธีนี้คือเราสามารถแทนที่คลาสใน getInstance () ตามกฎบางอย่าง

ฉันทำงานใน PHP ถ้าเป็นเช่นนั้น


ภาษาโปรแกรมอะไร
ริ้น

2
PHP (ทำไมมันถึงสำคัญ?)
Axel Foley

8
ดูเหมือนว่าคุณสามารถทำได้โดยใช้รูปแบบจากโรงงาน
superM

1
ดังที่ได้ทราบ: patern @superM ที่กล่าวถึงในโรงงานเป็นวิธีหนึ่งในการใช้ Inversion of Control (Dependency Injection) เป็นอีกวิธีหนึ่ง
Joachim Sauer

จาวาสคริปต์ใช้งานกับวัตถุอย่างคร่าวๆหรือไม่
Thijser

คำตอบ:


44

ฉันจะบอกว่านี่เป็นอุปสรรคต่อการออกแบบของคุณอย่างรุนแรง

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

ตัวอย่างเช่นถ้าทุกFooวัตถุต้องการFrobnicatorมันก็อาจจะตรวจสอบในการกำหนดว่าFrobnicatorเป็นที่ไม่เป็นโมฆะ หากคุณถอดคอนสตรัคเตอร์ออกไปมันก็ยากที่จะตรวจสอบ คุณตรวจสอบทุกจุดที่จะใช้หรือไม่? ในinit()วิธีการ (externalizing วิธีการกำหนด) ไม่เคยตรวจสอบและหวังว่าจะดีที่สุด?

ในขณะที่คุณอาจอาจจะยังคงดำเนินการทุกอย่าง (หลังจากทั้งหมดคุณยังคงทัวริงสมบูรณ์) บางสิ่งบางอย่างจะยากมากที่จะทำ

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


คุณหมายถึงอะไรโดย "หรือ" ปฏิเสธ "ตัวสร้างวัตถุ" แตกหัก "อย่างง่ายๆ
Geek

2
@Geek: คอนสตรัคเตอร์สามารถตรวจสอบอาร์กิวเมนต์ของมันและตัดสินใจว่าสิ่งเหล่านั้นจะส่งผลให้วัตถุทำงาน (ตัวอย่างเช่นถ้าวัตถุของคุณต้องการHttpClientแล้วมันจะตรวจสอบว่าพารามิเตอร์นั้นไม่ใช่โมฆะ) และหากข้อ จำกัด เหล่านั้นไม่เป็นไปตามนั้นก็สามารถโยนข้อยกเว้น นั่นเป็นไปไม่ได้จริงๆด้วยวิธีการสร้างและตั้งค่า
Joachim Sauer

1
ฉันคิดว่า OP เป็นเพียงการอธิบายการสร้างภายนอกของตัวสร้างภายในinit()ซึ่งเป็นไปได้ทั้งหมด - แม้ว่าสิ่งนี้จะกำหนดภาระการบำรุงรักษามากขึ้น
ปีเตอร์

26

2 ข้อดีสำหรับการก่อสร้าง:

คอนสตรัคเตอร์อนุญาตให้ทำตามขั้นตอนการก่อสร้างของวัตถุได้

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

หากสร้างอินสแตนซ์ที่Fooต้องการตั้งค่าคุณสมบัติ 3 ตัวตัวสร้างสามารถอ้างอิงทั้ง 3 ตรวจสอบความถูกต้องของพวกเขาและโยนข้อยกเว้นหากพวกเขาไม่ถูกต้อง

encapsulation

โดยอาศัยตัวตั้งค่าเพียงอย่างเดียวภาระนั้นขึ้นอยู่กับผู้บริโภคของวัตถุที่จะสร้างมันอย่างเหมาะสม อาจมีการรวมกันของคุณสมบัติที่แตกต่างกันที่ถูกต้อง

ยกตัวอย่างเช่นทุกFooเช่นความต้องการอย่างใดอย่างหนึ่งตัวอย่างของการBarเป็นทรัพย์สินbarหรือตัวอย่างของการเป็นทรัพย์สินBarFinder barFinderมันสามารถใช้อย่างใดอย่างหนึ่ง คุณสามารถสร้างนวกรรมิกสำหรับพารามิเตอร์ที่ถูกต้องทุกชุดและบังคับใช้อนุสัญญาด้วยวิธีดังกล่าว

ตรรกะและซีแมนทิกส์สำหรับวัตถุนั้นอาศัยอยู่ภายในวัตถุนั้น มันเป็น encapsulation ที่ดี


15

ใช่คุณสามารถอยู่ได้โดยปราศจากสิ่งก่อสร้าง

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

แต่ไม่คุณไม่ต้องการ 'ผู้สร้างของคุณเองอย่างเคร่งครัด แน่นอนว่าคุณไม่จำเป็นต้องมีคลาสและออบเจ็กต์อย่างเคร่งครัด

ทีนี้ถ้าเป้าหมายของคุณคือการใช้รูปแบบของโรงงานบางอย่างสำหรับการสร้างวัตถุนั่นไม่ได้เกิดจากการใช้ Constructor เมื่อเริ่มต้นวัตถุของคุณ


อย่างแน่นอน เราสามารถมีชีวิตอยู่ได้โดยไม่มีคลาสและวัตถุเริ่มต้นด้วย
JensG

5
ทุกคนทักทายผู้ยิ่งใหญ่!
Davor Ždralo

9

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

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

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

วิธีแก้ปัญหาคือการสร้างวัตถุเฉพาะผ่านวิธีการจากโรงงานซึ่งตรวจสอบทุกวัตถุที่สร้างขึ้นเพื่อความถูกต้องก่อนส่งคืนไปยังผู้โทร


3

$ obj = CLASS :: getInstance () จากนั้นเราฉีดการอ้างอิงโดยใช้ setters และทำการเริ่มต้นการเริ่มต้นโดยใช้ $ obj-> initInstance ();

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

$obj = new CLASS(array(
    'Frobnicator' => (),
    'Foonicator' => (),
));

และภายในนวกรรมิกคุณสามารถมั่นใจได้ถึงความสอดคล้องดังนี้

if (!array_key_exists('Frobnicator', $args)) {
    throw new Exception('Frobnicator required');
}
if (!array_key_exists('Foonicator', $args)) {
    $args['Foonicator'] = new DefaultFoonicator();
}

$args สามารถใช้เพื่อตั้งค่าสมาชิกส่วนตัวตามความจำเป็น

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


2

จริง ๆ แล้วฉันคิดเกี่ยวกับสิ่งที่คล้ายกัน

คำถามที่ฉันถามคือ "ตัวสร้างอะไรทำและเป็นไปได้ไหมที่จะทำแตกต่างกัน?" ฉันถึงข้อสรุปเหล่านั้น:

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

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

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

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

และฉันเพิ่งสังเกตเห็นคำตอบของฉันคือ OT ที่น่ารัก


2

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

ในภาษาที่ทันสมัย ​​(ฉันจะพูดถึงที่นี่เกี่ยวกับ C # ซึ่งฉันเขียนโปรแกรมไว้) คุณสามารถ จำกัด บางส่วนของรหัสที่สามารถทำงานได้เฉพาะในตัวสร้าง ขอบคุณที่คุณสามารถหลีกเลี่ยงข้อผิดพลาดเงอะงะ หนึ่งในสิ่งนั้นคือตัวแก้ไขแบบอ่านอย่างเดียว :

public class A {
    readonly string rostring;

    public A(string arg) {
        rostring = arg;
    }

    public static A CreateInstance(string arg) {
        var result = new A();
        A.rostring = arg;  // < because of this the code won't compile!
        return result;
    }
}

ในฐานะที่เป็นที่แนะนำโดยโจอาคิมซาวเออร์ก่อนหน้านี้แทนการใช้ลายออกแบบอ่านเกี่ยวกับFactory Dependency Injectionฉันจะขอแนะนำให้อ่านพึ่งพาการฉีดใน .NET โดย Mark Seemann


1

ยกตัวอย่างวัตถุที่มีประเภทขึ้นอยู่กับข้อกำหนดที่เป็นไปได้อย่างแน่นอน มันอาจเป็นวัตถุเองโดยใช้ตัวแปรทั่วโลกของระบบเพื่อกลับประเภทที่เฉพาะเจาะจง

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

* ฉันหมายถึงความจริงที่ว่าการทดสอบจะต้องพิจารณาประเภทแรกเป็นอันดับสองผลที่คุณต้องการที่จะบรรลุ จากนั้นคุณกำลังสร้างการทดสอบที่ซ้อนกันขนาดใหญ่


1

หากต้องการสร้างสมดุลของคำตอบอื่นให้อ้างสิทธิ์:

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

และ

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

ข้อความดังกล่าวบางครั้งบ่งบอกถึงข้อสันนิษฐานว่า:

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

แต่นี่ไม่เป็นความจริงเลย ภาษาส่วนใหญ่ไม่มีกฎต่อต้านคอนสตรัคเตอร์ที่ส่งผ่านthis( selfหรือสิ่งที่ภาษาเรียกว่า) ไปยังโค้ดภายนอก ตัวสร้างดังกล่าวปฏิบัติตามกฎที่ระบุไว้ข้างต้นอย่างสมบูรณ์ แต่ก็มีความเสี่ยงที่จะเปิดเผยวัตถุกึ่งสำเร็จรูปไปยังรหัสภายนอก มันเป็นจุดเล็ก ๆ น้อย ๆ แต่มองข้ามได้ง่าย


0

นี่เป็นเรื่องเล็ก ๆ น้อย ๆ แต่ฉันมักจะสงวนตัวสร้างสำหรับสถานะที่จำเป็นสำหรับวัตถุที่จะเสร็จสมบูรณ์และใช้งานได้ ยกเว้น setter-injection เมื่อตัวสร้างถูกเรียกใช้วัตถุของฉันควรจะสามารถทำงานที่ต้องการได้

สิ่งที่สามารถเลื่อนออกไปฉันออกจากตัวสร้าง (เตรียมค่าผลลัพธ์ ฯลฯ ) ด้วยวิธีการนี้ฉันไม่รู้สึกว่าใช้ constructors เพื่ออะไร แต่การฉีดแบบพึ่งพานั้นสมเหตุสมผล

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

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