รูปแบบ (anti) รูปแบบต่อไปนี้คืออะไร? อะไรคือข้อดีและข้อเสีย?


28

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

รูปแบบไปดังนี้:

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

public interface Vehicle {
    public void accelerate();
    public void decelerate();

    public static class Default {
         public static Vehicle getInstance() {
             return new Car(); // or use Spring to retrieve an instance
         }
    }
 }

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

 Vehicle someVehicle = Vehicle.Default.getInstance();
 someVehicle.accelerate();

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

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

ปรับปรุง:

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

public interface Vehicle {
      public void accelerate();
      public void decelerate();

      public static class Default {
           public static final Vehicle INSTANCE = getInstance();

           private static Vehicle getInstance() {
                return new Car(); // or use Spring/factory here
           }
      }
 }

 // Which allows to retrieve a singleton instance using...
 Vehicle someVehicle = Vehicle.Default.INSTANCE;

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


รูปแบบซิงเกิลแบบไหน?
ไบรอันเฉิน

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

7
โดยวิธีการVehicle.Default.getInstance() != Vehicle.Default.getInstance()
Konrad Morawski

20
antipattern ที่นี่คือการใช้ antipattern ที่มีความคล้ายคลึงกับรถยนต์ซึ่งเกี่ยวข้องกับ antipattern ที่คล้ายคลึงกันกับสัตว์ ซอฟต์แวร์ส่วนใหญ่ไม่มีล้อหรือขา
Pieter B

@PieterB: :-)))) คุณทำให้วันของฉัน!
Doc Brown

คำตอบ:


24

Default.getInstanceIMHO เป็นรูปแบบที่เฉพาะเจาะจงมากของวิธีการของโรงงานผสมกับแผนการตั้งชื่อที่นำมาจากรูปแบบซิงเกิล (แต่ไม่มีการนำไปใช้หลัง) ในรูปแบบปัจจุบันนี่เป็นการละเมิด " หลักการความรับผิดชอบเดี่ยว " เนื่องจากส่วนต่อประสาน (ซึ่งทำหน้าที่จุดประสงค์ในการประกาศคลาส API) จะรับผิดชอบเพิ่มเติมในการจัดหาอินสแตนซ์เริ่มต้นซึ่งจะอยู่ในส่วนที่แยกต่างหากVehicleFactoryชั้น

ปัญหาหลักที่เกิดจากการสร้างนี้ก็คือว่ามันก่อให้เกิดการพึ่งพาระหว่างวงจรและVehicle Carตัวอย่างเช่นในรูปแบบปัจจุบันมันเป็นไปไม่ได้ที่จะวางVehicleในห้องสมุดหนึ่งและCarในอีก การใช้คลาสโรงงานแยกต่างหากและการวางDefault.getInstanceวิธีการนั้นจะช่วยแก้ปัญหานั้นได้ มันอาจเป็นความคิดที่ดีที่จะให้ชื่อนั้นมีชื่อที่แตกต่างกันเพื่อป้องกันความสับสนที่เกี่ยวข้องกับซิงเกิลตัน ดังนั้นผลลัพธ์อาจเป็นเช่นนั้น

VehicleFactory.createDefaultInstance()


ขอบคุณสำหรับคำตอบ! ฉันยอมรับว่าตัวอย่างเฉพาะนี้เป็นการละเมิด SRP อย่างไรก็ตามฉันไม่แน่ใจว่าจะยังคงเป็นการละเมิดหรือไม่ในกรณีที่มีการเรียกใช้งานแบบไดนามิก เช่นโดยใช้ Spring (หรือเฟรมเวิร์กที่คล้ายกัน)เพื่อดึงข้อมูลอินสแตนซ์เฉพาะที่กำหนดโดยตัวอย่างเช่นไฟล์กำหนดค่า
Jérôme

1
@ Jérôme: IMHO คลาสที่ไม่ซ้อนกันที่มีชื่อVehicleFactoryนั้นมีโครงสร้างมากกว่าโครงสร้างที่ซ้อนกันVehicle.Defaultโดยเฉพาะกับวิธีการ "getInstance" ที่ทำให้เข้าใจผิด แม้ว่ามันจะเป็นไปได้ที่จะหลีกเลี่ยงการพึ่งพาแบบวนซ้ำโดยใช้ Spring แต่ส่วนตัวแล้วฉันจะชอบตัวแปรตัวแรกเพียงเพราะสามัญสำนึก และยังคงทำให้คุณมีตัวเลือกในการย้ายโรงงานไปยังส่วนประกอบที่แตกต่างกันเปลี่ยนการใช้งานหลังจากนั้นไปทำงานโดยไม่ต้องใช้สปริง ฯลฯ
Doc Brown

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

หากรถยนต์เป็นยานพาหนะที่ใช้งานทั่วไปได้มาตรฐานนี่ไม่ใช่การละเมิด SRP ยานพาหนะยังคงมีเพียงเหตุผลเดียวที่เปลี่ยนแปลงเช่นเมื่อ Google เพิ่มautodrive()วิธีการ รถยนต์ทั่วไปของคุณจะต้องเปลี่ยนไปใช้วิธีการนั้นเช่นโดยการโยน NotImplementedException แต่ไม่ได้ให้เหตุผลเพิ่มเติมในการเปลี่ยนยานพาหนะ
user949300

@ user949300: SRP ไม่ใช่แนวคิด "คณิตศาสตร์ที่พิสูจน์ได้" และการตัดสินใจออกแบบซอฟต์แวร์ไม่ใช่ "ขาวดำ" เสมอไป รหัสต้นฉบับนั้นไม่เลวเลยที่ไม่สามารถใช้ในรหัสการผลิตแน่นอนและอาจมีบางกรณีที่ความสะดวกเมื่อเทียบกับการพึ่งพาแบบวนรอบเนื่องจาก OP ได้ระบุไว้ใน "การอัพเดท" ของตัวเอง ดังนั้นโปรดอ่านคำตอบของฉันเป็น "พิจารณาว่าการใช้คลาสแยกจากโรงงานไม่ได้ให้บริการคุณดีกว่า" หมายเหตุผู้เขียนเปลี่ยนคำถามเดิมของเขาเล็กน้อยหลังจากที่ฉันตอบคำถาม
Doc Brown

3

ดูเหมือนว่ารูปแบบวัตถุ Nullสำหรับฉัน

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

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


2
สวัสดีและขอบคุณสำหรับคำตอบ แม้ว่าฉันค่อนข้างแน่ใจว่าในกรณีของฉันนี่ไม่ได้ใช้ในการปรับใช้รูปแบบ "วัตถุ Null" มันเป็นคำอธิบายที่น่าสนใจ
Jérôme
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.