หลักการแยกอินเตอร์เฟสใช้กับวิธีที่เป็นรูปธรรมหรือไม่?


10

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

แต่วิธีการที่เป็นรูปธรรม? ฉันควรแยกวิธีการที่ลูกค้าไม่ทุกคนจะใช้หรือไม่ พิจารณาคลาสต่อไปนี้:

public class Car{
    ....

    public boolean isQualityPass(){
        ...
    }

    public int getTax(){
        ...
    }

    public int getCost(){
        ...
    }
}

public class CarShop{
    ...
    public int getCarPrice(int carId){
        Car car=carList[carId];
        int price=car.getTax() + car.getCost()... (some formula);
        return price;
    }
}

ที่โค้ดด้านบน CarShop ไม่ได้ใช้วิธี isQualityPass () ใน Car เลยฉันควรแยก isQualityPass () ออกเป็นคลาสใหม่:

public class CheckCarQualityPass{
    public boolean isQualityPass(Car car){
    }
}

เพื่อลดข้อต่อของ CarShop เพราะฉันคิดว่าถ้า isQualityPass () ต้องการการพึ่งพาพิเศษเช่น:

public boolean isQualityPass(){
    HttpClient client=...
}

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


2
รถมักจะรู้หรือไม่เมื่อผ่าน "คุณภาพ" หรืออาจเป็นกฎเกณฑ์ทางธุรกิจที่อาจถูกห่อหุ้มด้วยตัวของมันเอง?
Laiv

2
ในฐานะที่เป็นคำอินเตอร์เฟซที่ใช้ใน ISP หมายถึงมันเป็นเรื่องของการเชื่อมต่อ ดังนั้นถ้าคุณมีวิธีการในของคุณCarชั้นเรียนที่คุณไม่ต้องการ (ทั้งหมด) ผู้ใช้สามารถรู้เกี่ยวกับการสร้าง (มากกว่าหนึ่ง) อินเตอร์เฟซที่Carดำเนินคลาสซึ่งประกาศเพียงวิธีการที่เป็นประโยชน์ในบริบทของอินเตอร์เฟซ
Timothy Truckle

@ Laiv ฉันแน่ใจว่าเราจะเห็นยานพาหนะที่รู้มากกว่านั้นในไม่ช้า ;)
แซนวิชการสร้างแบบจำลองแบบครบวงจร

1
รถยนต์จะรู้ว่าผู้ผลิตต้องการให้เขารู้อะไร โฟล์คสวาเก้นรู้ว่าสิ่งที่ฉันกำลังพูดถึง :-)
Laiv

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

คำตอบ:


6

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

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

หากisQualityPassไม่สามารถย้ายออกไปเรียนอื่นได้อย่างง่ายดายทางเลือกอื่นคือทำการHttpโทรผ่านอินเทอร์เฟซแบบนามธรรมIHttpClientซึ่งถูกฉีดเข้าไปCarในเวลาก่อสร้างหรือโดยการฉีดกลยุทธ์การตรวจสอบ "QualityPass" ทั้งหมด (ด้วยการร้องขอ Http ที่ห่อหุ้ม) ลงในCarวัตถุ . แต่นั่นเป็นเพียง IMHO ทางออกที่ดีที่สุดลำดับที่สองเนื่องจากมันเพิ่มความซับซ้อนโดยรวมแทนที่จะลดลง


รูปแบบกลยุทธ์เกี่ยวกับการแก้ไขวิธี isQualityPass คืออะไร
Laiv

@Laiv: ในทางเทคนิคสิ่งนี้จะใช้ได้แน่นอน (ดูการแก้ไขของฉัน) แต่มันจะส่งผลให้Carวัตถุที่ซับซ้อนมากขึ้น มันจะไม่เป็นตัวเลือกแรกของฉันสำหรับการแก้ปัญหา (อย่างน้อยก็ไม่ได้อยู่ในบริบทของตัวอย่างที่วางแผนไว้นี้) อย่างไรก็ตามมันอาจสมเหตุสมผลกว่าในรหัส "ของจริง" ฉันไม่รู้
Doc Brown

6

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

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

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

public class CarShop{
    ...
    public int getCarPrice(int carId){
        Car car=carList[carId];
        int price=car.getTax() + car.getCost()... (some formula);
        return price;
    }
}

สิ่งที่ใช้ที่นี่เป็นและgetTax() สิ่งที่ถูกยืนกรานที่จะเป็นทุกอย่างผ่านเข้าถึงgetCost() CarปัญหาคือการยืนยันในCarหมายถึงการยืนยันในการเข้าถึงisQualityPass()ที่ไม่จำเป็น

สิ่งนี้สามารถแก้ไขได้ คุณถามว่าสามารถแก้ไขได้อย่างเป็นรูปธรรมหรือไม่ มันสามารถ

public class CarShop{
    ...
    public int getCarPrice(int carId){
        CarLiability carLiability=carLiabilityList[carId];
        int price=carLiability.getTax() + carLiability.getCost()... (some formula);
        return price;
    }
}

ไม่มีรหัสใดที่รู้ว่าCarLiabilityเป็นอินเทอร์เฟซหรือคลาสที่เป็นรูปธรรม นั่นเป็นสิ่งที่ดี ไม่อยากรู้

หากเป็นอินเทอร์เฟซCarอาจใช้งานได้ นี้จะไม่ละเมิด ISP เพราะแม้ว่าisQuality()อยู่ในCar CarShopไม่ยืนยันในมัน นี่เป็นเรื่องปกติ

หากเป็นรูปธรรมอาจเป็นได้ว่าisQuality()ไม่มีอยู่หรือย้ายไปที่อื่น นี่เป็นเรื่องปกติ

มันอาจเป็นได้ว่าCarLiabilityเป็นเสื้อคลุมคอนกรีตรอบ ๆCarที่มอบหมายงานให้กับมัน ตราบCarLiabilityใดที่ไม่เปิดเผยisQuality()ก็CarShopถือว่าใช้ได้ ของหลักสูตรนี้เพิ่งเตะกระป๋องลงและCarLiabilityต้องคิดวิธีการติดตาม ISP ด้วยCarวิธีเดียวกับที่CarShopต้องทำ

ในระยะสั้นisQuality()ไม่จำเป็นต้องถูกลบออกCarเนื่องจาก ISP ความต้องการโดยนัยสำหรับisQuality()ความต้องการที่จะลบออกCarShopเพราะCarShopไม่ต้องการดังนั้นจึงไม่ควรขอ


4

หลักการแยกอินเตอร์เฟสใช้กับวิธีที่เป็นรูปธรรมหรือไม่?

แต่วิธีการที่เป็นรูปธรรม? ฉันควรแยกวิธีการที่ลูกค้าไม่ทุกคนจะใช้หรือไม่

ไม่ได้จริงๆ มีวิธีการที่แตกต่างกันเพื่อซ่อนอยู่จากCar.isQualityPassCarShop

1. การปรับเปลี่ยนการเข้าถึง

จากกฎหมาย Demeter ของมุมมองของเราอาจจะพิจารณาCarและCardShopจะไม่เป็นเพื่อน มันทำให้เราชอบธรรมในการทำสิ่งต่อไป

package com.my.package.domain.model
public class Car{
    ...
    protected boolean isQualityPass(){...}
}

package com.my.package.domain.services
public class CarShop{
    ...
}

ระวังส่วนประกอบทั้งสองอยู่ในแพ็คเกจที่ต่างกัน ตอนนี้CarShopไม่สามารถมองเห็นพฤติกรรมที่Car ได้รับการป้องกัน (ขอโทษล่วงหน้าถ้าตัวอย่างข้างต้นดูเรียบง่าย)

2. การแยกส่วนต่อประสาน

ISPทำงานจากสมมติฐานที่ว่าเราทำงานร่วมกับนามธรรมไม่ได้กับการเรียนที่เป็นรูปธรรม ฉันจะสมมติว่าคุณคุ้นเคยกับการติดตั้ง ISP และบทบาทอินเตอร์เฟสแล้ว

แม้จะมีการCarใช้งานจริงไม่มีอะไรขัดขวางเราจากการฝึกใช้ ISP

//role interfaces 
public interface Billable{
   public int getCosts();
   public int getTaxs();
}

//role interfaces
public interface QualityAssurance{
   public boolean isQualityPass();
}

public class Car implements Billable, QualityAssurance{
   ...
}

public class CarShop {
  ...
  public int getPrice(Billable billable){
     return billable.getCosts() * billable.getTaxs();
  }
}

สิ่งที่ฉันทำที่นี่ ฉันได้ จำกัด การโต้ตอบระหว่างCarและCarShopผ่านบทบาทที่เรียกเก็บเงินได้ที่เรียกเก็บเงินได้ ระวังการเปลี่ยนแปลงของgetPriceลายเซ็น ฉันได้แก้ไขข้อโต้แย้งโดยเจตนา ฉันต้องการทำให้ชัดเจนว่าCarShop"ผูก / ผูก" กับหนึ่งในอินเตอร์เฟสบทบาทที่มีอยู่เท่านั้น ฉันสามารถติดตามการใช้งานจริง แต่ฉันไม่ทราบรายละเอียดการใช้งานจริงและฉันกลัวว่าจริง ๆ แล้วgetPrice(String carId)มีการเข้าถึง (การมองเห็น) เหนือชั้นรูปธรรม ถ้ามันมีทุกงานที่ทำกับ ISP จะกลายเป็นไร้ประโยชน์เพราะมันอยู่ในมือของนักพัฒนาที่จะทำหล่อและการทำงานเท่านั้นที่มีอินเตอร์เฟซที่เรียกเก็บเงิน ไม่ว่าเราจะมีระเบียบแบบไหนการล่อลวงก็จะอยู่ที่นั่นเสมอ

3. ความรับผิดชอบเดี่ยว

ฉันเกรงว่าฉันไม่ได้อยู่ในฐานะที่จะบอกว่าการพึ่งพาระหว่างCarและHttpClientเพียงพอ แต่ฉันเห็นด้วยกับ @DocBrown มันทำให้เกิดคำเตือนบางอย่างที่คุ้มค่ากับการทบทวนการออกแบบ กฎหมายของ Demeter และ ISP จะไม่ทำให้การออกแบบของคุณ "ดีขึ้น" ในตอนนี้ พวกเขาจะปกปิดปัญหาไม่ใช่แก้ไข

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

สรุป

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

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