“ ส่วนต่อประสานคงที่” เป็นแนวปฏิบัติที่ดีหรือไม่?


13

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

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

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

public interface Constants {
    String LOG_MESSAGE_FAILURE = "I took an arrow to the knee.";
    int DEFAULT_TIMEOUT_MS = 30;
}

คุณสามารถสร้างบางสิ่งที่ซับซ้อนกว่านี้ได้เช่นคีย์หลอกหลอก enum ของ config

public interface ConfigKeys {
    static createValues(ConfigKey<?>... values) {
        return Collections.unmodifiableSet(new HashSet(Arrays.asList(values)));
    }

    static ConfigKey<T> key(Class<T> clazz) {
        return new ConfigKey<>(clazz);
    }

    class ConfigKey<T> {
        private final Class<T> type;
        private ConfigKey(Class<T> type) {
            this.type = type;
        }
        private Class<T> getType() {
            return type;
        }
    }
}

import static ConfigKeys.*;
public interface MyAppConfigKeys {
    ConfigKey<Boolean> TEST_MODE = key(Boolean.class);
    ConfigKey<String> COMPANY_NAME = key(String.class);

    Set<ConfigKey<?>> VALUES = createValues(TEST_MODE, COMPANY_VALUE);

    static values() {
        return VALUES;
    }
}

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

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

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

ฉันสนใจในเหตุผล (ไม่ใช่) มากกว่าที่จะใช้รูปแบบเช่นสองคนนี้


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


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

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

มันอาจสร้างความสับสนให้กับนักพัฒนาใหม่ที่พยายามที่จะจัดการกับแนวคิดพื้นฐานของ OOP เช่น Interfaces, AbstractClases, เมื่อต้องใช้การจัดองค์ประกอบแทนการสืบทอด, ... ฯลฯ
เอเดรีย

Aliester คุณกำลังพูดถึงdefaultวิธีการ ฉันกำลังพูดถึงstaticวิธีการและเขตข้อมูล สิ่งเหล่านั้นไม่ได้รับมรดกดังนั้นพวกเขาจะไม่ทำลายมรดกหลายอย่าง
Vlasec

คำตอบ:


1

ตัวอย่างง่ายๆเป็นเพียงซองจดหมายสำหรับค่าคงที่ทั่วโลก

การใส่ค่าคงที่บนอินเทอร์เฟซเรียกว่าConstant Interface Antipatternเนื่องจากค่าคงที่มักจะเป็นเพียงรายละเอียดการนำไปใช้งาน ข้อบกพร่องต่อไปจะสรุปในบทความวิกิพีเดียในอินเตอร์เฟซที่คงที่

คุณสามารถสร้างยูทิลิตี้ "คลาส" ด้วยวิธีนี้

อันที่จริงเอกสาร Javaระบุว่าวิธีการคงที่สามารถทำให้ง่ายขึ้นในการจัดระเบียบวิธีการช่วยเหลือเช่นเมื่อเรียกวิธีการช่วยเหลือจากวิธีการเริ่มต้น


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

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

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

1

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

  • ไม่สามารถขยายคลาสได้โดยไม่super()ต้องโทร แต่สามารถ "ใช้งาน" อินเตอร์เฟสได้
  • ไม่สามารถสร้างอินสแตนซ์ของคลาสได้หากไม่มีการสะท้อนในขณะที่อินเทอร์เฟซสามารถสร้างอินสแตนซ์ของคลาสแบบไม่ระบุชื่อได้อย่างง่ายดายเช่นนี้: new MyInterface() {};

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

นอกจากนี้ยังมีสิ่งแปลก ๆ เกี่ยวกับการสืบทอดจาก "ส่วนต่อประสานคงที่":

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

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


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

0

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

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


ใช่การนำเข้าคงที่เสียงเหมือนความคิดที่ดีกว่าการสืบทอดของค่าคงที่และใช้งานได้กับสมาชิกแบบคงที่ของคลาสและอินเทอร์เฟซ และสามารถมองเห็นได้ภายในคลาส / อินเตอร์เฟสเท่านั้น
Vlasec

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

ฉันเห็นมูลค่าเป็นค่าคงที่บนส่วนต่อประสาน อินเทอร์เฟซมีแนวโน้มที่จะกำหนดวิธีการและวิธีการบางครั้งยอมรับค่าคงที่เป็นพารามิเตอร์ interface ColorFilter {Image applyGradient(int colorSpace, int width, byte[] pixels); }เช่น: ฉันสามารถจินตนาการมีการคงแตกต่างกันบางRGB, RGBA, CMYK, GREYSCALE, INDEXEDฯลฯ
Stijn เดอวิตต์

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

0

ฉันจะบอกว่าถ้าคุณสนใจวิธีการใช้คุณสมบัติใหม่อย่างถูกต้องลองดูที่รหัสใน JDK วิธีการเริ่มต้นบนอินเทอร์เฟซถูกใช้อย่างกว้างขวางและหาง่าย แต่วิธีสแตติกบนอินเตอร์เฟสดูเหมือนจะผิดปกติมาก ตัวอย่าง ได้แก่java.time.chrono.Chronology, java.util.Comparator, java.util.Mapและค่อนข้างไม่กี่อินเตอร์เฟซในและjava.util.functionjava.util.stream

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

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


-1

ฉันสงสัยว่ามีเหตุผลที่จะไม่ใช้มัน

แล้วความเข้ากันได้กับ JVM รุ่นเก่าจะเป็นอย่างไร

คุณคิดว่าเป็นความคิดที่ดีโดยทั่วไปหรือไม่?

ไม่มันเป็นการเปลี่ยนแปลงด้านหลังที่เข้ากันไม่ได้ซึ่งเพิ่ม zilch ให้กับการแสดงออกของภาษา นิ้วโป้งลงสองนิ้ว

มีบางสถานการณ์จำลองที่คุณแนะนำคลาสอย่างยิ่งหรือไม่

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

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


ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
maple_shaft

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