ความแตกต่างระหว่างรูปแบบสะพานและรูปแบบอะแดปเตอร์


126

ความแตกต่างระหว่างรูปแบบ Bridge และ Adapter คืออะไร?


อาจพิจารณาเสนอการแก้ไขคำชี้แจงเพื่อเป็นแนวทางในการอภิปรายเกี่ยวกับจุดที่คุณเชื่อว่าคุณต้องใช้อย่างใดอย่างหนึ่ง
Jeff Wilcox


คำตอบ:


173

"อะแดปเตอร์ทำให้สิ่งต่างๆใช้งานได้หลังจากออกแบบสะพานทำให้ใช้งานได้ก่อนที่จะเป็น [GoF, p219]"

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

public class SuperWeaponsArray {
  /*...*/

  public void destroyWorld() {
    for (Weapon w : armedWeapons) {
      w.fire();
    }
  }
}

ยิ่งใหญ่ ยกเว้นเราตระหนักว่าเรามีอุปกรณ์นิวเคลียร์ในคลังแสงของเราซึ่งมีมาก่อนการแปลงเป็นอินเทอร์เฟซ Weapon แต่เราอยากให้มันทำงานที่นี่จริงๆ ... แล้วเราจะทำยังไง ... ลิ่มเข้าไป!

NukeWeaponsAdaptor - อิงจากคลาส Nuke ของเรา แต่ส่งออกอินเทอร์เฟซ Weapon น่ารักตอนนี้เราสามารถทำลายโลกได้อย่างแน่นอน ดูเหมือนจะเป็นเศษเล็กเศษน้อย แต่ก็ทำให้สิ่งต่างๆทำงานได้ดี


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

ประเภท MemoryMappedFile และ DirectReadFile ของอ็อบเจ็กต์ไฟล์ สมมติว่าคุณต้องการอ่านไฟล์จากแหล่งต่างๆ (อาจใช้ Linux เทียบกับการใช้งาน Windows ฯลฯ ) Bridge ช่วยให้คุณหลีกเลี่ยงการคดเคี้ยวด้วย:

หน่วยความจำแมป Windows หน่วยความจำไฟล์แมปลินุกซ์ไฟล์โดยตรงอ่าน Windows ไฟล์โดยตรงอ่านลินุกซ์ไฟล์


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

36
@omouse upvoted โค้ดตัวอย่างไม่ใช่สิ่งที่ทำให้คำตอบนี้ตรงประเด็น สำหรับผู้อ่านอย่างรอบคอบมีตัวชี้เพียงพอที่จะเริ่มแยกแยะรูปแบบดังนั้นโดยรวมแล้วมันเป็นคำตอบที่ดี
Victor Farazdagi

15
คุณช่วยให้ตัวอย่างรหัสจริงสำหรับรูปแบบสะพานได้ไหม
Jaime Hablutzel

2
ฉันจินตนาการว่ามีคนจำนวนมากมาที่คำถามนี้แบบเดียวกับที่ฉันทำ - พวกเขาอาจกำลังมองหาโค้ดสำหรับทั้งสองรูปแบบอยู่แล้ว แต่รับรู้ถึงความคล้ายคลึงกันบางอย่างและตระหนักว่าความเข้าใจของพวกเขาสามารถทำให้แน่นแฟ้นยิ่งขึ้นได้โดยการวางสองรูปแบบ บรรทัดเกี่ยวกับบริดจ์ที่ช่วยให้คุณหลีกเลี่ยงการคดเคี้ยวกับไฟล์เฉพาะของ Windows และ Linux อย่างน้อยสำหรับฉันก็มีประโยชน์ในการทำความเข้าใจว่า "Implementor" ของ Bridge Pattern ( dofactory.com/net/bridge-design-pattern ) แตกต่างจาก "อะแดปเตอร์"
จอร์แดน

3
"อะแดปเตอร์ทำให้สิ่งต่างๆใช้งานได้หลังจากออกแบบสะพานทำให้ใช้งานได้ก่อนที่จะเป็น" ไม่ได้ระบุไว้ในหนังสือที่ฉันอ่านเลยดังนั้นจึงยากที่จะแยกแยะทั้งสองอย่าง ฉันเดาว่าการอ่าน GOF คุ้มค่ากับความพยายาม ...
Alexander Derck

15

http://en.wikipedia.org/wiki/Adapter_pattern

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

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

http://en.wikipedia.org/wiki/Bridge_pattern

รูปแบบ Bridge จะช่วยให้คุณสามารถใช้อัลกอริทึมหรือระบบทางเลือกอื่นได้

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

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


14

อะแดปเตอร์:

  1. มันเป็นรูปแบบโครงสร้าง
  2. มีประโยชน์ในการทำงานกับอินเทอร์เฟซที่เข้ากันไม่ได้สองแบบ

UML Diagram:จากบทความdofactory :

ใส่คำอธิบายภาพที่นี่

Target : กำหนดอินเทอร์เฟซเฉพาะโดเมนที่ไคลเอ็นต์ใช้

Adapter : ปรับอินเทอร์เฟซ Adaptee กับอินเทอร์เฟซ Target

Adaptee : กำหนดอินเทอร์เฟซที่มีอยู่ซึ่งจำเป็นต้องปรับเปลี่ยน

ไคลเอนต์ : ทำงานร่วมกับอ็อบเจ็กต์ที่สอดคล้องกับอินเทอร์เฟซ Target

ตัวอย่าง:

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

public class AdapterDemo{
    public static void main(String args[]){
        SquareArea s = new SquareArea(4);
        System.out.println("Square area :"+s.getArea());
    }
}

class RectangleArea {
    public int getArea(int length, int width){
        return length * width;
    }
}

class SquareArea extends RectangleArea {

    int length;
    public SquareArea(int length){
        this.length = length;
    }
    public int getArea(){
        return getArea(length,length);
    }
}

สะพาน:

  1. เป็นรูปแบบโครงสร้าง
  2. มันแยกสิ่งที่เป็นนามธรรมออกจากการนำไปใช้และทั้งสองอย่างอาจแตกต่างกันอย่างอิสระ
  3. เป็นไปได้เพราะมีการใช้องค์ประกอบแทนการถ่ายทอดทางพันธุกรรม

แก้ไข: (ตามคำแนะนำของ @quasoft)

คุณมีองค์ประกอบสี่ส่วนในรูปแบบนี้

  1. Abstraction : เป็นการกำหนดอินเทอร์เฟซ

  2. RefinedAbstraction : ใช้นามธรรม:

  3. Implementor : กำหนดอินเทอร์เฟซสำหรับการนำไปใช้งาน

  4. ConcreteImplementor : ใช้อินเทอร์เฟซ Implementor

ข้อมูลโค้ด:

Gear gear = new ManualGear();
Vehicle vehicle = new Car(gear);
vehicle.addGear();

gear = new AutoGear();
vehicle = new Car(gear);
vehicle.addGear();

โพสต์ที่เกี่ยวข้อง:

คุณใช้ Bridge Pattern เมื่อใด แตกต่างจากรูปแบบ Adapter อย่างไร?

ความแตกต่างที่สำคัญ:จากsourcemakingบทความ

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

รวมตัวอย่างรถยนต์ / รถบรรทุก / เกียร์จากเอกสารในคำตอบ ตัวอย่างที่ดีและการเปรียบเทียบ
quasoft

8

กระทู้นี้มีมาสักพักแล้ว อย่างไรก็ตามสิ่งสำคัญคือต้องเข้าใจว่าส่วนหน้านั้นค่อนข้างคล้ายกับอะแดปเตอร์ แต่ก็ไม่เหมือนกัน อะแด็ปเตอร์ "ปรับ" คลาสที่มีอยู่ให้เป็นคลาสไคลเอนต์ที่เข้ากันไม่ได้ สมมติว่าคุณมีระบบเวิร์กโฟลว์เก่าที่แอปพลิเคชันของคุณใช้เป็นไคลเอนต์ บริษัท ของคุณอาจแทนที่ระบบเวิร์กโฟลว์ด้วยระบบใหม่ที่ "เข้ากันไม่ได้" (ในแง่ของอินเทอร์เฟซ) ในกรณีส่วนใหญ่คุณสามารถใช้รูปแบบอะแด็ปเตอร์และเขียนโค้ดที่เรียกใช้อินเทอร์เฟซของกลไกเวิร์กโฟลว์ใหม่ได้ โดยทั่วไปสะพานมักใช้ในลักษณะที่แตกต่างกัน หากคุณมีระบบที่ต้องทำงานกับระบบไฟล์อื่น (เช่นโลคัลดิสก์ NFS เป็นต้น) คุณสามารถใช้รูปแบบบริดจ์และสร้างเลเยอร์นามธรรมหนึ่งชั้นเพื่อทำงานกับระบบไฟล์ทั้งหมดของคุณ โดยพื้นฐานแล้วจะเป็นกรณีการใช้งานที่เรียบง่ายสำหรับรูปแบบสะพาน Facade และอะแดปเตอร์ใช้คุณสมบัติบางอย่างร่วมกัน แต่โดยปกติ facades จะใช้เพื่อลดความซับซ้อนของอินเทอร์เฟซ / คลาสที่มีอยู่ ในช่วงแรก ๆ ของ EJB ไม่มีการเรียกร้องให้ EJB ในท้องถิ่น นักพัฒนามักจะได้รับต้นขั้วโดยย่อให้แคบลงและเรียกมันว่า "หลอกจากระยะไกล" บ่อยครั้งนี้ทำให้เกิดปัญหาด้านประสิทธิภาพ (โดยเฉพาะเมื่อเรียกผ่านสายไฟจริงๆ) นักพัฒนาที่มีประสบการณ์จะใช้รูปแบบด้านหน้าเพื่อจัดเตรียมอินเทอร์เฟซที่หยาบมากให้กับลูกค้า จากนั้นซุ้มนี้จะทำการเรียกหลายครั้งไปยังวิธีการแบบละเอียดที่แตกต่างกัน สรุปแล้วสิ่งนี้ช่วยลดจำนวนการเรียกเมธอดที่ต้องการลงอย่างมากและเพิ่มประสิทธิภาพ


แม้ว่าดูเหมือนจะอยู่นอกขอบเขตของคำถามนี้ แต่การถ่วงน้ำหนัก Adapter & Bridge กับ Facade อาจเหมาะสมมาก
Cody

1

Bridge ได้รับการปรับปรุง Adapter Bridge มีอะแดปเตอร์และเพิ่มความยืดหยุ่นเพิ่มเติมให้กับมัน องค์ประกอบจากแผนผังคำตอบของ Ravindra ระหว่างรูปแบบมีดังนี้

      Adapter  |    Bridge
    -----------|---------------
    Target     | Abstraction
    -----------|---------------
               | RefinedAbstraction
               |
               |   This element is Bridge specific. If there is a group of 
               |   implementations that share the same logic, the logic can be placed here.
               |   For example, all cars split into two large groups: manual and auto. 
               |   So, there will be two RefinedAbstraction classes.
    -----------|--------------- 
    Adapter    | Implementor
    -----------|---------------
    Adaptee    | ConcreteImplementor

1

ในคำตอบด้านบน @James อ้างอิงประโยคจาก GoF หน้า 219 ฉันคิดว่ามันคุ้มค่าที่จะอธิบายคำอธิบายทั้งหมดที่นี่

อะแดปเตอร์เทียบกับบริดจ์

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

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

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


0

สมมติว่าคุณมีคลาส Shape นามธรรมที่มีฟังก์ชันการวาดภาพ (ทั่วไป / นามธรรม) และวงกลมที่ใช้ Shape รูปแบบสะพานเป็นวิธีนามธรรมสองทางในการแยกการใช้งาน (การวาดในวงกลม) และฟังก์ชันทั่วไป / นามธรรม (การวาดภาพในคลาส Shape)

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

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


0

ดูเหมือนคำตอบที่สั้นและชัดเจนสำหรับฉันตามคำตอบ stackoverflow อื่นที่นี่ :

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

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

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