การห่อหุ้มยังคงเป็นหนึ่งในช้าง OOP ที่ยืนอยู่บน?


97

Encapsulation บอกให้ฉันสร้างฟิลด์ทั้งหมดหรือเกือบทั้งหมดเป็นส่วนตัวและแสดงสิ่งเหล่านี้โดย getters / setters แต่ตอนนี้ห้องสมุดเช่นลอมบอก@Dataปรากฏซึ่งช่วยให้เราเพื่อแสดงเขตข้อมูลส่วนตัวทั้งหมดของบันทึกย่อสั้น มันจะสร้าง getters, setters และการตั้งค่าคอนสตรัคเตอร์สำหรับฟิลด์ส่วนตัวทั้งหมด

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

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

วงจรนี้มีความรู้สึกอย่างไร? และมีการห่อหุ้มจริงๆความรู้สึกใด ๆ ในขณะนี้ในการเขียนโปรแกรมในชีวิตจริง?


19
"It will create getters, setters and setting constructors for all private fields."- วิธีที่คุณอธิบายเครื่องมือนี้ดูเหมือนว่าจะยังคงมีการห่อหุ้ม (อย่างน้อยก็ในแบบหลวม ๆ แบบอัตโนมัติและแบบจำลองโลหิตจาง) ดังนั้นปัญหาคืออะไร
David

78
การห่อหุ้มคือการซ่อนการใช้งานภายในของวัตถุที่อยู่เบื้องหลังสัญญาสาธารณะของตน (โดยปกติจะเป็นส่วนต่อประสาน) Getters และ setters ทำสิ่งที่ตรงกันข้าม - พวกเขาเปิดเผย internals ของวัตถุดังนั้นปัญหาอยู่ใน getters / setters ไม่ใช่การห่อหุ้ม

22
@VinceEmigh เรียนข้อมูลที่ไม่มีการห่อหุ้ม อินสแตนซ์ของพวกเขาคือค่าในแง่ที่ว่าดั้งเดิมคือ
Caleth

10
@VinceEmigh ใช้ JavaBeans คือไม่ OOมันเป็นขั้นตอน วรรณคดีเรียกพวกเขาว่า "วัตถุ" เป็นความผิดพลาดของประวัติศาสตร์
Caleth

28
ฉันใช้ความคิดมากในเรื่องนี้ตลอดหลายปีที่ผ่านมา ฉันคิดว่านี่เป็นกรณีที่เจตนาของ OOP แตกต่างจากการใช้งาน หลังจากเรียน SmallTalk มันชัดเจนว่าการห่อหุ้มของ OOP นั้นตั้งใจจะทำอะไร (เช่นแต่ละคลาสจะเป็นคอมพิวเตอร์อิสระด้วยวิธีการเป็นโปรโตคอลที่ใช้ร่วมกัน) แต่ด้วยเหตุผลที่ไม่เป็นที่รู้จักสำหรับฉัน วัตถุ getter / setter ที่ไม่มีการห่อหุ้มแนวความคิด (พวกมันไม่ซ่อนอะไรจัดการอะไรเลยและไม่มีความรับผิดชอบใด ๆ นอกจากข้อมูล) และพวกมันยังคงใช้คุณสมบัติ
jrh

คำตอบ:


82

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

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

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

การแก้ไข

หลังจากเห็นการอภิปรายที่เกิดขึ้นฉันต้องการเพิ่มหลายสิ่ง:

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

ดังนั้นถ้าเรากลับไปที่คำถามที่ว่าทำไมเราควรทำเช่นนี้ ลองพิจารณาตัวอย่างง่ายๆนี้:

public class Document {
    private String title;

    public String getTitle() {
        return title;
    }
}

public class SomeDocumentServiceOrHandler {

    public void printDocument(Document document) {
        System.out.println("Title is " + document.getTitle());
    }
}

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

public interface Printable {
    void print();
}

public final class PrintableDocument implements Printable {
    private final String title;

    public PrintableDocument(String title) {
        this.title = title;
    }

    @Override
    public void print() {
        System.out.println("Title is " + title);
    }
}

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


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

1
"Getters / setters ไม่ดีเท่าทุ่งสาธารณะธรรมดา" - นั่นไม่เป็นความจริงอย่างน้อยก็ในหลายภาษา โดยทั่วไปคุณไม่สามารถแทนที่การเข้าถึงฟิลด์ธรรมดา แต่สามารถแทนที่ getters / setters ที่ให้ความคล่องตัวในการเรียนเด็ก นอกจากนี้ยังทำให้ง่ายต่อการเปลี่ยนพฤติกรรมของคลาส เช่นคุณอาจเริ่มต้นด้วย getter / setter ที่ได้รับการสนับสนุนจากเขตข้อมูลส่วนตัวและจากนั้นย้ายไปยังเขตข้อมูลอื่นหรือคำนวณค่าจากค่าอื่น ๆ ไม่สามารถทำได้ด้วยฟิลด์ธรรมดา บางภาษาอนุญาตให้เขตข้อมูลมีตัวรับและตัวตั้งค่าอัตโนมัติเป็นหลัก แต่ Java ไม่ใช่ภาษาดังกล่าว
Kat

เอ๊ะยังไม่เชื่อ ตัวอย่างของคุณไม่เป็นไร แต่เพียงแสดงถึงรูปแบบการเข้ารหัสที่แตกต่างไม่ใช่ "จริงถูก" - ซึ่งไม่มีอยู่ โปรดทราบว่าภาษาส่วนใหญ่มีความหลากหลายในปัจจุบันและไม่ค่อยมีความบริสุทธิ์ OO ou ขั้นตอนที่บริสุทธิ์ ยึดมั่นในตัวคุณเองอย่างหนักเพื่อ "แนวคิด OO บริสุทธิ์" เป็นตัวอย่าง - คุณไม่สามารถใช้ DI บนตัวอย่างของคุณได้เนื่องจากคุณเชื่อมโยงตรรกะการพิมพ์กับคลาสย่อย การพิมพ์ไม่ควรเป็นส่วนหนึ่งของเอกสาร - printableDocument จะเปิดเผยส่วนที่พิมพ์ได้ (ผ่านทะลุ) ไปยัง PrinterService
ต. Sar

วิธีการ OO ที่เหมาะสมหากเราใช้วิธี "Pure OO" จะใช้บางสิ่งที่ใช้คลาส PrinterService ที่เป็นนามธรรมซึ่งขอผ่านทางข้อความสิ่งที่ควรพิมพ์ - ใช้ GetPrintablePart สิ่งที่ดำเนินการ PrinterService อาจเป็นเครื่องพิมพ์ประเภทใดก็ได้ - พิมพ์เป็น PDF ไปยังหน้าจอไปยัง TXT ... โซลูชันของคุณทำให้ไม่สามารถสลับตรรกะการพิมพ์สำหรับสิ่งอื่น ๆ ได้สิ้นสุดคู่กันมากขึ้นและบำรุงรักษาน้อยลงกว่าตัวอย่าง "ผิด" ของคุณ
ต. Sar

บรรทัดล่าง: Getters และ Setters ไม่ใช่คนชั่วหรือทำลาย OOP - คนที่ไม่เข้าใจวิธีใช้พวกมันคือ กรณีตัวอย่างของคุณคือตัวอย่างตำราเรียนของคนที่ขาดจุดทั้งหมดว่า DI ทำงานอย่างไร ตัวอย่างที่คุณ "แก้ไข" ถูกเปิดใช้งาน DI, แยก, สามารถถูกเยาะเย้ยได้อย่างง่ายดาย ... จริง ๆ - จำสิ่งที่ "ชอบองค์ประกอบมากกว่ามรดก" ทั้งหมดหรือไม่ หนึ่งในเสาหลัก OO? คุณเพียงแค่โยนมันไปที่หน้าต่างโดยวิธีที่คุณ refactored รหัส สิ่งนี้จะไม่เกิดขึ้นในการตรวจสอบโค้ดที่ร้ายแรงใด ๆ
ต. Sar

76

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

ความรู้สึกคือการที่คุณไม่ควรจะทำอย่างนั้น

การห่อหุ้มหมายความว่าคุณเปิดเผยเฉพาะเขตข้อมูลเหล่านั้นที่คุณต้องการใช้คลาสอื่นเพื่อเข้าถึงและคุณเลือกและระมัดระวังมาก

อย่าไม่เพียงแค่ให้ทุกสาขา getters และ setters ต่อเริ่มต้น!

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

แต่ถ้าคุณดูข้อมูลจำเพาะคุณจะเห็นว่ามันตั้งใจสร้าง getters และ setters ให้เลือกมากและมันพูดถึงคุณสมบัติ "อ่านอย่างเดียว" (ไม่มีตัวตั้ง) และคุณสมบัติ "เขียนอย่างเดียว" (ไม่มี ทะเยอทะยาน)

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

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


20
"[G] เอตเตอร์และเซ็ตเตอร์ไม่จำเป็นต้องเข้าถึงได้ง่าย" - ฉันคิดว่านั่นเป็นประเด็นสำคัญ หากรหัสของคุณกำลังเข้าถึงฟิลด์ตามชื่อคุณจะไม่สามารถเปลี่ยนพฤติกรรมได้ในภายหลัง หากคุณใช้ obj.get () คุณสามารถ
Dan Ambrogio

4
@jrh: ฉันไม่เคยได้ยินมันเรียกว่าการปฏิบัติที่ไม่ดีใน Java และมันเป็นเรื่องธรรมดา
Michael Borgwardt

2
@MichaelBorgwardt น่าสนใจ; หัวข้อเล็กน้อย แต่ฉันสงสัยอยู่เสมอว่าทำไม Microsoft แนะนำให้ทำเช่นนั้นสำหรับ C # ฉันเดาว่าแนวทางเหล่านั้นบอกเป็นนัยว่าใน C # มีเพียงสิ่งเดียวที่ผู้ใช้สามารถใช้เพื่อแปลงค่าที่กำหนดให้กับค่าอื่น ๆ ที่ใช้เป็นการภายในในวิธีที่ไม่สามารถล้มเหลวหรือมีค่าที่ไม่ถูกต้อง (เช่นมีคุณสมบัติ PositionInches และคุณสมบัติ มันจะแปลงภายในเป็นมิลลิเมตรโดยอัตโนมัติ) ไม่ใช่ตัวอย่างที่ดี แต่เป็นสิ่งที่ดีที่สุดที่ฉันสามารถทำได้ในตอนนี้
jrh

2
@jrh แหล่งที่มาบอกว่าจะไม่ทำเพื่อ getters ซึ่งตรงข้ามกับ setters
Captain Man

3
@Gangnus และคุณเห็นว่ามีใครบางคนซ่อนตรรกะบางอย่างในทะเยอทะยาน / สุนัขเซทเทอร์? เราคุ้นเคยกับมันgetFieldName()เป็นสัญญาอัตโนมัติสำหรับเรา เราจะไม่คาดหวังพฤติกรรมที่ซับซ้อนบางอย่างที่อยู่เบื้องหลัง ในกรณีส่วนใหญ่มันเป็นเพียงการทำลายของการห่อหุ้ม
Izbassar Tolegen

17

ฉันคิดว่าประเด็นสำคัญของเรื่องนี้ถูกอธิบายโดยความคิดเห็นของคุณ:

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

ปัญหาที่คุณมีอยู่ว่าคุณกำลังผสมติดตา DataModel กับการใช้งาน DataModel

แอปพลิเคชันโดยทั่วไปจะมีหลาย datamodels:

  • datamodel เพื่อพูดคุยกับฐานข้อมูล
  • ดาต้าโมเดลเพื่ออ่านไฟล์กำหนดค่า
  • datamodel เพื่อพูดคุยกับแอพพลิเคชันอื่น
  • ...

ที่ด้านบนของ datamodel ที่จริงแล้วใช้เพื่อทำการคำนวณ

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

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

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

ในทางกลับกัน BOM ของคุณควรมีค่าคงที่ซึ่งโดยทั่วไปจะห้ามการมี setters จำนวนมาก (ผู้ที่ไม่ได้รับผลกระทบจากค่าคงที่แม้ว่าพวกเขาจะลดการห่อหุ้มลงไปถึงระดับ)


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

3
@immibis: ฉันเห็นด้วยโดยทั่วไปอย่างไรก็ตามตามที่ระบุไว้โดย OP กรอบงานบางอย่างจำเป็นต้องได้รับ / setters ซึ่งในกรณีนี้คุณเพียงแค่ต้องปฏิบัติตาม โปรดทราบว่า OP กำลังใช้ไลบรารีซึ่งสร้างขึ้นมาโดยอัตโนมัติด้วยแอปพลิเคชั่นของคุณลักษณะเดียวดังนั้นมันจึงใช้ได้ผลน้อยสำหรับเขา
Matthieu M.

1
@Gangnus: ความคิดที่นี่คือ getters / setters ถูกแยกไปที่เลเยอร์ขอบเขต (I / O) ซึ่งเป็นเรื่องปกติที่จะได้รับความสกปรก แต่ส่วนที่เหลือของแอปพลิเคชัน (รหัสบริสุทธิ์) ไม่ได้ปนเปื้อนและยังคงสง่างาม
Matthieu M.

2
@kubanczyk: คำจำกัดความเป็นทางการคือ Business Object Model เท่าที่ฉันรู้ มันตรงกับสิ่งที่ฉันเรียกว่า "inner datamodel" ซึ่งเป็น datamodel ที่คุณเรียกใช้ตรรกะในแกน "pure" ของแอปพลิเคชันของคุณ
Matthieu M.

1
นี่คือวิธีการปฏิบัติ เราอยู่ในโลกไฮบริด หากห้องสมุดภายนอกสามารถรู้ว่าข้อมูลใดที่จะส่งผ่านไปยังตัวสร้าง BOM ดังนั้นเราจะมี BOM เท่านั้น
Adrian Iftode

12

พิจารณาดังต่อไปนี้

คุณมีUserคลาสที่มีคุณสมบัติint age:

class User {
    int age;
}

คุณต้องการที่จะขยายขนาดนี้เพื่อให้Userมีวันเดือนปีเกิดซึ่งตรงข้ามกับอายุเท่านั้น ใช้ทะเยอทะยาน:

class User {
    private int age;

    public int getAge() {
        return age;
    }
}

เราสามารถสลับint ageฟิลด์เพื่อความซับซ้อนมากขึ้นLocalDate dateOfBirth:

class User {
    private LocalDate dateOfBirth;

    public int getAge() {
        LocalDate now = LocalDate.now();
        int year = ...; // calculate using dateOfBirth and now
        return year;
    }

    // other behaviors can now make use of dateOfBirth
}

ไม่มีการละเมิดสัญญาไม่มีการแตกรหัสไม่มีอะไรมากไปกว่าการขยายการเป็นตัวแทนภายในเพื่อเตรียมพร้อมสำหรับพฤติกรรมที่ซับซ้อนมากขึ้น

ฟิลด์นั้นถูกห่อหุ้ม


ตอนนี้เพื่อล้างข้อกังวล ..

ลอมบอกของ@Dataคำอธิบายประกอบคล้ายกับ Kotlin ของชั้นเรียนข้อมูล

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

Encapsulation ใช้เพื่อซ่อนค่าหรือสถานะของวัตถุข้อมูลที่มีโครงสร้างภายในคลาส

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

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

ลอมบอกยังให้การสนับสนุน@Getterและ@Setterเป็นอิสระ - ใช้สิ่งที่คุณต้องการ


2
สิ่งนี้ไม่เกี่ยวข้องกับการตบ@Dataคำอธิบายประกอบบนประเภทซึ่งการออกแบบจะไม่มีการ
เข้ารหัส

1
ไม่มีเขตข้อมูลจะไม่ถูกซ่อน เพราะทุกอย่างสามารถเข้ามาได้และsetAge(xyz)
Caleth

9
@Caleth ฟิลด์ถูกซ่อนอยู่ คุณกำลังทำตัวราวกับว่า setters ไม่สามารถมีเงื่อนไขล่วงหน้าและโพสต์ได้ซึ่งเป็นกรณีการใช้งานทั่วไปสำหรับการใช้ setters คุณกำลังทำหน้าที่เหมือนเป็นเขตข้อมูลคุณสมบัติ == ซึ่งแม้ในภาษาเช่น C # ก็ไม่เป็นความจริงเพราะทั้งสองมีแนวโน้มที่จะได้รับการสนับสนุน (เช่นใน C #) สามารถย้ายฟิลด์ไปยังคลาสอื่นได้สามารถสลับเป็นตัวแทนที่ซับซ้อนมากขึ้น ...
Vince Emigh

1
และสิ่งที่ฉันพูดคือไม่สำคัญ
Caleth

2
@ Caleth มันไม่สำคัญอย่างไร มันไม่สมเหตุสมผลเลย คุณกำลังบอกว่าการห่อหุ้มไม่ได้ใช้เพราะคุณรู้สึกว่ามันไม่สำคัญในสถานการณ์นี้แม้ว่าจะใช้โดยคำจำกัดความและมีการใช้เคส
Vince Emigh

11

Encapsulation บอกให้ฉันสร้างฟิลด์ทั้งหมดหรือเกือบทั้งหมดเป็นส่วนตัวและแสดงสิ่งเหล่านี้โดย getters / setters

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

การห่อหุ้มจึงเป็นกรณีพิเศษของการซ่อนข้อมูลซึ่งแต่ละวัตถุจะซ่อนภายในและบังคับให้มีค่าคงที่

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

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

ส่วนหนึ่งเกิดจากอุบัติเหตุครั้งประวัติศาสตร์ Strike one คือใน Java, นิพจน์การเรียกใช้เมธอดและนิพจน์การเข้าถึงฟิลด์จะแตกต่างกันในไซต์การโทรนั่นคือการเปลี่ยนการเข้าถึงฟิลด์โดยการเรียกใช้ getter หรือ setter เป็นการแบ่ง API ของคลาส ดังนั้นหากคุณอาจต้องใช้ตัวเข้าถึงคุณต้องเขียนหนึ่งตัวในตอนนี้หรือสามารถทำลาย API ได้ กรณีที่ไม่มีการนี้การสนับสนุนคุณสมบัติภาษาระดับคือในคมชัดกับภาษาที่ทันสมัยอื่น ๆ ที่สะดุดตาที่สุดC #และECMAScript

Strike 2 คือข้อกำหนดคุณสมบัติJavaBeans ที่ กำหนดไว้เป็น getters / setters เขตข้อมูลไม่ใช่คุณสมบัติ ดังนั้นเฟรมเวิร์กขององค์กรส่วนใหญ่จึงสนับสนุน getters / setters แต่ไม่ใช่ฟิลด์ นานมาแล้วในตอนนี้ ( Java Persistence API (JPA) , การตรวจสอบความถูกต้องของถั่ว , สถาปัตยกรรม Java สำหรับการผูก XML (JAXB) , แจ็คสันเขตข้อมูลสนับสนุนทั้งหมดใช้ได้ดีในตอนนี้) แต่บทเรียนและหนังสือเก่ายังคงมีอยู่อย่างต่อเนื่องและไม่ใช่ทุกคนที่ตระหนักว่าสิ่งต่าง ๆ มีการเปลี่ยนแปลง การขาดการสนับสนุนคุณสมบัติระดับภาษาอาจยังคงเป็นปัญหาในบางกรณี (ตัวอย่างเช่นเนื่องจาก JPA การโหลดที่ขี้เกียจของเอนทิตีเดี่ยวไม่ได้เรียกเมื่ออ่านฟิลด์สาธารณะ) แต่ฟิลด์สาธารณะส่วนใหญ่ทำงานได้ดี หากต้องการปัญญา บริษัท ของฉันเขียน DTO ทั้งหมดสำหรับ REST APIs ของพวกเขาด้วยฟิลด์สาธารณะ (มันไม่ได้เผยแพร่ต่อสาธารณะมากขึ้นที่ส่งผ่านอินเทอร์เน็ตหลังจาก :-) ทั้งหมด

ที่กล่าวว่าลอมบอกของ@Dataไม่มากกว่าการสร้าง getters / setters: มันยังสร้างtoString(), hashCode()และequals(Object)ซึ่งสามารถมีค่าค่อนข้าง

และตอนนี้มีการห่อหุ้มความรู้สึกในการเขียนโปรแกรมจริง ๆ หรือไม่?

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

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

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

สรุป

getters / setters เสนอการห่อหุ้มค่อนข้างแย่

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

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


1
บังคับค่าคงที่หรือไม่? มันเป็นภาษาอังกฤษเหรอ? คุณช่วยอธิบายให้คนที่ไม่ใช่คนอังกฤษได้ไหม ไม่ซับซ้อนเกินไป - บางคนที่นี่ไม่รู้ภาษาอังกฤษดีพอ
Gangnus

ฉันชอบพื้นฐานของคุณฉันชอบความคิดของคุณ (+1) แต่ไม่ใช่ผลในทางปฏิบัติ ฉันอยากจะใช้ฟิลด์ส่วนตัวและห้องสมุดพิเศษบางอย่างสำหรับการโหลดข้อมูลสำหรับพวกเขาโดยการไตร่ตรอง ฉันไม่เข้าใจว่าทำไมคุณตั้งค่าคำตอบของคุณเป็นข้อโต้แย้งกับฉัน
Gangnus

@Gangnus ค่าคงที่เป็นเงื่อนไขแบบลอจิคัลที่ไม่เปลี่ยนแปลง (ในความหมายไม่ใช่และ - ความหมายแปรเปลี่ยนแตกต่างกัน / เปลี่ยนแปลง) ฟังก์ชั่นจำนวนมากมีกฎที่จะต้องเป็นจริงก่อนและหลังพวกเขาดำเนินการ (เรียกว่า pre-conditions และ post-conditions) อื่น ๆ มีข้อผิดพลาดในรหัส ตัวอย่างเช่นฟังก์ชั่นอาจต้องการให้อาร์กิวเมนต์ไม่เป็นโมฆะ (เป็นเงื่อนไขล่วงหน้า) และอาจโยนข้อยกเว้นหากอาร์กิวเมนต์นั้นเป็นโมฆะเพราะกรณีนั้นจะเป็นข้อผิดพลาดในรหัส (เช่นในวิทยาการคอมพิวเตอร์พูดรหัสได้ทำลาย คงที่)
Pharap

@Gangus: ไม่แน่ใจว่าที่สะท้อนเข้าสู่การสนทนานี้; ลอมบอกเป็นตัวประมวลผลคำอธิบายประกอบเช่นปลั๊กอินไปยังคอมไพเลอร์ Java ที่ปล่อยโค้ดเพิ่มเติมระหว่างการคอมไพล์ แต่แน่นอนคุณสามารถใช้ลอมบอกได้ ฉันแค่บอกว่าในหลายกรณีเขตข้อมูลสาธารณะทำงานได้ดีและติดตั้งง่ายกว่า (ไม่ใช่คอมไพเลอร์ทั้งหมดที่ตรวจจับตัวประมวลผลคำอธิบายประกอบโดยอัตโนมัติ ... )
meriton

1
@Gangnus: ค่าคงที่เหมือนกันในวิชาคณิตศาสตร์และ CS แต่ใน CS มีมุมมองเพิ่มเติม: จากมุมมองของวัตถุค่าคงที่จะต้องบังคับใช้และเป็นที่ยอมรับ จากมุมมองของผู้โทรของวัตถุนั้นค่าคงที่จะเป็นจริงเสมอ
meriton

7

ฉันถามตัวเองด้วยคำถามนั้นอย่างแน่นอน

มันไม่เป็นความจริงทั้งหมด ความชุกของ getters / setters IMO นั้นเกิดจากข้อกำหนดของ Java Bean ซึ่งจำเป็นต้องใช้ ดังนั้นมันจึงเป็นคุณสมบัติที่ไม่ใช่การเขียนโปรแกรม Object Oriented แต่การเขียนโปรแกรม Bean oriented หากคุณต้องการ ความแตกต่างระหว่างสองสิ่งนี้คือเลเยอร์นามธรรมที่มีอยู่ ถั่วเป็นอินเทอร์เฟซระบบมากกว่าบนเลเยอร์ที่สูงกว่า พวกเขาสรุปจากรากฐานของ OO หรืออย่างน้อยก็เช่นเคยสิ่งต่าง ๆ กำลังถูกขับออกมาบ่อยเกินไป

ฉันจะบอกว่ามันค่อนข้างโชคร้ายที่สิ่ง Bean นี้ที่แพร่หลายในการเขียนโปรแกรม Java ไม่ได้มาพร้อมกับการเพิ่มคุณสมบัติภาษา Java ที่สอดคล้องกัน - ฉันคิดว่าบางอย่างเช่นแนวคิดคุณสมบัติใน C # สำหรับผู้ที่ไม่ทราบว่ามันเป็นโครงสร้างภาษาที่มีลักษณะเช่นนี้:

class MyClass {
    string MyProperty { get; set; }
}

อย่างไรก็ตามการใช้งานจริงยังคงได้รับประโยชน์อย่างมากจากการห่อหุ้ม


Python มีคุณสมบัติที่คล้ายกันซึ่งคุณสมบัติสามารถมี getters / setters โปร่งใส ฉันแน่ใจว่ามีภาษาอื่น ๆ อีกมากมายที่มีคุณสมบัติคล้ายกันเช่นกัน
JAB

1
"ความชุกของ getters / setters IMO นั้นเกิดจากข้อกำหนดของ Java Bean ซึ่งจำเป็นต้องใช้" ใช่คุณพูดถูก และใครบอกว่ามันจำเป็นต้องมี? เหตุผลได้ไหม
Gangnus

2
วิธี C # นั้นเหมือนกับลอมบอกอย่างแน่นอน ฉันไม่เห็นความแตกต่างที่แท้จริง ความสะดวกสบายในทางปฏิบัติมากขึ้น แต่เห็นได้ชัดว่าแย่ในความคิด
Gangnus

1
@Gangnis สเปคต้องการมัน ทำไม? ตรวจสอบคำตอบของฉันสำหรับตัวอย่างที่เป็นรูปธรรมว่าทำไม getters จึงไม่เหมือนกับฟิลด์เปิดเผย - ลองทำเหมือนกันโดยไม่ต้องทะลวง
Vince Emigh

@VinceEmigh gangnUs โปรด :-) แก๊งค์เดินนัส - นัท) และสิ่งที่เกี่ยวกับการใช้รับ / การตั้งค่าเฉพาะที่เราต้องการมันเป็นพิเศษและการโหลดจำนวนมากลงในช่องส่วนตัวโดยการสะท้อนกลับในกรณีอื่น ๆ ?
Gangnus

6

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

คำตอบง่ายๆคือ: คุณพูดถูก Getters และ setters กำจัดค่า encapsulation ส่วนใหญ่ (แต่ไม่ใช่ทั้งหมด) นี่ไม่ได้เป็นการบอกว่าเมื่อใดก็ตามที่คุณได้รับและ / หรือวิธีการตั้งค่าที่คุณกำลังทำลาย encapsulation แต่ถ้าคุณเพิ่ม accessor ให้กับสมาชิกส่วนตัวในชั้นเรียนของคุณ

ใช่มีเทคโนโลยีอื่น ๆ ที่ทำงานผ่าน getters และ setters และเราไม่สามารถใช้มันผ่านช่องสาธารณะที่เรียบง่าย แต่เทคโนโลยีเหล่านี้ปรากฏขึ้นเพราะเรามีคุณสมบัติมากมายเหล่านั้น - ฟิลด์ส่วนตัวที่อยู่เบื้องหลังผู้ได้รับ / setters สาธารณะ หากเราไม่มีคุณสมบัติเทคโนโลยีเหล่านี้จะพัฒนาอีกวิธีหนึ่งและสนับสนุนเขตข้อมูลสาธารณะ และทุกอย่างจะง่ายและเราไม่ต้องการลอมบอกในตอนนี้ วงจรนี้มีความรู้สึกอย่างไร? และตอนนี้มีการห่อหุ้มความรู้สึกจริงๆหรือยัง

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

ป้อนคำอธิบายรูปภาพที่นี่

ใช้ตัวอย่างถั่ว

นี่ไม่ใช่ความคิดที่น่ากลัว แต่ฉันไม่เคยเป็นแฟนตัวยงของแนวทางใน Java มันจะไปขัดกับเมล็ดข้าว ใช้ Python, Groovy เป็นต้นสิ่งที่สนับสนุนวิธีการนี้อย่างเป็นธรรมชาติ

สิ่ง JavaBean ชนิดหมุนตัวออกจากการควบคุมเพราะมันสร้าง JOBOL เช่น Java เขียนนักพัฒนาที่ไม่เข้าใจ OO โดยทั่วไปวัตถุก็ไม่ได้เป็นอะไรมากไปกว่าถุงข้อมูลและตรรกะทั้งหมดนั้นถูกเขียนไว้ภายนอกด้วยวิธีการที่ยาวนาน เพราะมันถูกมองว่าเป็นเรื่องปกติผู้คนอย่างคุณและฉันที่ถามว่านี่เป็นกุ๊ก เมื่อเร็ว ๆ นี้ฉันได้เห็นการเปลี่ยนแปลงและนี่ไม่ใช่ตำแหน่งนอกโลกมากนัก

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


2
ความคิดที่เป็นรูปธรรมและฉลาดที่สุด +1 แต่ทำไมไม่เขียนกลุ่มของคลาสที่ทุกคนจะโหลด / บันทึกฟิลด์ส่วนตัวไปยัง / จากโครงสร้างภายนอกบางอย่างหนาแน่น JSON, XML, วัตถุอื่น ๆ มันสามารถทำงานกับ POJO หรืออาจจะเฉพาะกับเขตข้อมูลที่ทำเครื่องหมายโดยคำอธิบายประกอบสำหรับสิ่งนั้น ตอนนี้ฉันกำลังคิดถึงทางเลือกนั้น
Gangnus

@Gangnus ที่เดาได้เพราะนั่นหมายถึงการเขียนรหัสพิเศษจำนวนมาก หากมีการใช้การสะท้อนจะมีการเขียนรหัสซีเรียลไลซ์เซชั่นเพียงก้อนเดียวเท่านั้นและสามารถเขียนคลาสใด ๆ ที่ตามรูปแบบเฉพาะได้ C # รองรับสิ่งนี้ผ่าน[Serializable]คุณสมบัติและญาติของมัน เหตุใดจึงต้องเขียนวิธีการซีเรียลไลซ์เซชัน 101 เฉพาะ / คลาสเมื่อคุณสามารถเขียนได้โดยใช้การสะท้อนกลับ
Pharap

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

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

@Phap นั่นคือสิ่งที่ฉันพูดถึง แต่ทำไมคุณถึงเรียกมันว่า "การใช้ประโยชน์" และทางเลือกที่ฉันพูดถึงในความคิดเห็นแรกที่นี่ยังคงอยู่ - ฟิลด์ที่บรรจุควรมีการเพิ่มความคิดเห็นพิเศษหรือไม่?
Gangnus

5

เราจะสามารถทำอะไรได้มากโดยไม่ต้องได้รับ เป็นไปได้ไหมที่จะลบออกอย่างสมบูรณ์? สิ่งนี้สร้างปัญหาอะไรบ้าง เราสามารถแบนreturnคำสำคัญได้หรือไม่

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

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

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

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

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

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

อาจมีลักษณะเช่นนี้:

ป้อนคำอธิบายภาพที่นี่

class Interactor implements InputPort {
    OutputPort out;
    int y = 0;

    Interactor(OutputPort out){
        this.out = out;
    }

    void accumulate(int x) {
        y = y + x;
        out.showAsImage(y);
    }
}

หากคุณสามารถโค้ดเช่นนั้นการใช้ getters เป็นตัวเลือก ไม่ใช่ความชั่วร้ายที่จำเป็น


ใช่ getters / setters มีความรู้สึกที่ดี แต่คุณเข้าใจหรือไม่ว่าเป็นเรื่องอื่น :-) +1 อยู่ดี
Gangnus

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

5

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

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

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

แต่ถ้าผู้ทะเยอทะยาน / ผู้ตั้งค่าเพียงสะท้อนเขตข้อมูลส่วนบุคคลพื้นฐานไม่ว่ามันจะแย่ใช่ไหม?

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

แต่ไม่ได้รับการตั้งค่าอัตโนมัติ getters / setters จากเขตทำลาย encapsulation?

ไม่มีการห่อหุ้มยังคงอยู่ที่นั่น! @Dataคำอธิบายประกอบเป็นเพียงวิธีที่สะดวกในการเขียน getter / setter-pairs ซึ่งใช้ฟิลด์พื้นฐาน สำหรับมุมมองของลูกค้านี่เป็นเหมือนคู่ getter / setter ปกติ หากคุณตัดสินใจที่จะเขียนการใช้งานใหม่คุณยังสามารถทำได้โดยไม่กระทบกับลูกค้า ดังนั้นคุณจะได้รับสิ่งที่ดีที่สุดของโลกทั้งสอง: การห่อหุ้มและไวยากรณ์ที่กระชับ

แต่บางคนก็พูดว่าผู้ทะเลาะกัน

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


การห่อหุ้มควรทำให้เรามีความหลากหลายและความปลอดภัย ได้รับ / ชุดอนุญาตให้ภาคเรียนที่ แต่อย่างยิ่งต่อที่สอง
Gangnus

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

1
"GangNus" :-) สัปดาห์ที่ผ่านมาฉันได้ทำงานในโครงการที่เต็มไปด้วยการเรียก getters และ setters ในหลายชั้น มันไม่ปลอดภัยอย่างยิ่งและยิ่งแย่กว่านั้นก็รองรับคนในการเข้ารหัสที่ไม่ปลอดภัย ฉันดีใจที่ฉันเปลี่ยนงานเร็วพอสำหรับคนที่คุ้นเคยกับวิธีนี้ ดังนั้นผมไม่คิดว่ามันฉันเห็นมัน
Gangnus

2
@jrh: ฉันไม่รู้ว่ามีคำอธิบายอย่างเป็นทางการเช่นนี้หรือไม่ แต่ C # มีการรองรับคุณสมบัติในตัว (getters / setters ที่มีไวยากรณ์ nicer) และสำหรับประเภทที่ไม่ระบุตัวตนซึ่งเป็นประเภทที่มีคุณสมบัติเท่านั้นและไม่มีพฤติกรรม ดังนั้นนักออกแบบของ C # จึงแยกออกจากการเปรียบเทียบการส่งข้อความและ "บอกไม่ต้องถาม" อย่างจงใจ การพัฒนา C # ตั้งแต่เวอร์ชั่น 2.0 ดูเหมือนจะได้รับแรงบันดาลใจจากภาษาหน้าที่มากกว่าความบริสุทธิ์ดั้งเดิมของ OO
JacquesB

1
@jrh: ตอนนี้ฉันเดา แต่ฉันคิดว่า C # ค่อนข้างได้รับแรงบันดาลใจจากภาษาที่ใช้งานได้ซึ่งข้อมูลและการดำเนินงานนั้นแยกจากกันมากขึ้น ในสถาปัตยกรรมแบบกระจายมุมมองได้เปลี่ยนจากข้อความที่มุ่งเน้น (RPC, SOAP) เป็นข้อมูลที่มุ่งเน้น (REST) และฐานข้อมูลที่เปิดเผยและทำงานกับข้อมูล (ไม่ใช่วัตถุ "กล่องดำ") ได้รับชัยชนะ ในระยะสั้นฉันคิดว่าโฟกัสได้เปลี่ยนจากข้อความระหว่างกล่องดำไปเป็นตัวแบบข้อมูลที่เปิดเผย
JacquesB

3

การห่อหุ้มนั้นมีจุดประสงค์ แต่สามารถนำไปใช้ในทางที่ผิดหรือใช้ในทางที่ผิด

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

ในทางกลับกัน POD หรือชนิดข้อมูลเก่าแบบเดิมเช่นโครงสร้างจาก C / C ++ ซึ่งเขตข้อมูลทั้งหมดเป็นสาธารณะจะมีประโยชน์เช่นกัน การมีผู้ได้รับ / ผู้ตั้งค่าที่ไร้ประโยชน์เช่นเดียวกับที่สร้างโดยคำอธิบายประกอบ @data ในลอมบอกเป็นเพียงวิธีในการรักษา "รูปแบบการห่อหุ้ม" หนึ่งในไม่กี่เหตุผลที่เราทำ "ไร้ประโยชน์" getters / setters ใน Java เป็นว่าวิธีการที่ให้สัญญา

ใน Java คุณไม่สามารถมีฟิลด์ในอินเทอร์เฟซได้ดังนั้นคุณใช้ getters และ setters เพื่อระบุคุณสมบัติทั่วไปที่ implementers ทั้งหมดของอินเตอร์เฟสนั้นมี ในภาษาล่าสุดเช่น Kotlin หรือ C # เราเห็นแนวคิดของคุณสมบัติเป็นเขตข้อมูลที่คุณสามารถประกาศตัวตั้งค่าและตัวรับ ในท้ายที่สุดตัวรับสัญญาณ / ตัวเซ็ตที่ไร้ประโยชน์เป็นมรดกตกทอดมากกว่าที่ Java มีอยู่เว้นแต่ว่า Oracle จะเพิ่มคุณสมบัติเข้าไป ตัวอย่างเช่น Kotlin ซึ่งเป็นภาษา JVM อื่นที่พัฒนาโดย JetBrains มีคลาสข้อมูลซึ่งโดยทั่วไปแล้วจะทำในสิ่งที่หมายเหตุประกอบ @data ทำในลอมบอก

นี่คือตัวอย่างบางส่วน:

class DataClass 
{
    private int data;

    public int getData() { return data; }
    public void setData(int data) { this.data = data; } 
}

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

class DataClass implements IDataInterface
{
    private int data;

    @Override public int getData() { return data; }
    @Override public void setData(int data) { this.data = data; }
}

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

void doSomethingWithData(IDataInterface data) { data.setData(...); }

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

สมมติว่าคุณมีคลาสผู้เล่นที่ใช้การห่อหุ้มและต้องการย้ายตำแหน่งของเขา 1 หน่วย รหัสจะมีลักษณะเช่นนี้:

player.setPosX(player.getPosX() + 1);

มันจะมีลักษณะเช่นนี้:

player.posX++;

ที่นี่เขาให้เหตุผลว่า encapsulations นำไปสู่การพิมพ์ที่มากขึ้นโดยไม่มีผลประโยชน์เพิ่มเติมและในหลายกรณีสามารถเป็นจริง แต่สังเกตเห็นบางสิ่งบางอย่าง อาร์กิวเมนต์ขัดแย้งกับไวยากรณ์ไม่ใช่การห่อหุ้มตัวเอง แม้ในภาษาอย่าง C ที่ขาดแนวคิดเรื่องการห่อหุ้มคุณก็มักจะเห็นตัวแปรใน structs prexifed หรือ sufixed ด้วย '_' หรือ 'my' หรืออะไรก็ตามที่บ่งบอกว่าพวกเขาไม่ควรใช้ผู้บริโภคของ API ราวกับว่าพวกเขาเป็น เอกชน.

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

class VerticalList implements ...
{
    private int posX;
    private int posY;
    ... //other members

    public void setPosition(int posX, int posY)
    {
        //change position and move all the objects in the list as well
    }
}

หากตัวแปรเป็นแบบสาธารณะในตัวอย่างนี้ผู้บริโภคของ API นี้จะสับสนว่าเมื่อใดที่จะใช้ posX และ posY และเมื่อใดควรใช้ setPosition () ด้วยการซ่อนรายละเอียดเหล่านั้นคุณจะช่วยให้ผู้บริโภคใช้ API ของคุณได้ง่ายขึ้น

ไวยากรณ์เป็นข้อ จำกัด ในหลายภาษาแม้ว่า อย่างไรก็ตามภาษาที่ใหม่กว่านำเสนอคุณสมบัติซึ่งทำให้เรามีไวยากรณ์ที่ดีของสมาชิก publice และประโยชน์ของการห่อหุ้ม คุณจะพบคุณสมบัติใน C #, Kotlin แม้ใน C ++ หากคุณใช้ MSVC นี่คือตัวอย่างใน Kotlin

คลาส VerticalList: ... {var posX: Int set (x) {field = x; ... } var posY: Int set (y) {field = y; ... }}

ที่นี่เราประสบความสำเร็จเช่นเดียวกับในตัวอย่าง Java แต่เราสามารถใช้ posX และ posY ราวกับว่าพวกเขาเป็นตัวแปรสาธารณะ เมื่อฉันพยายามที่จะเปลี่ยนค่าของพวกเขาแม้ว่าร่างกายของชุดตัวตั้งค่า () จะถูกดำเนินการ

ใน Kotlin สิ่งนี้จะเทียบเท่ากับ Java Bean ที่มี getters, setters, hashcode, เท่ากับและ toString ที่นำมาใช้:

data class DataClass(var data: Int)

ขอให้สังเกตว่าไวยากรณ์นี้ช่วยให้เราสามารถ Java Bean ในหนึ่งบรรทัด คุณสังเกตเห็นปัญหาที่ภาษาเช่น Java มีอย่างถูกต้องในการใช้การห่อหุ้ม แต่นั่นเป็นความผิดของ Java ที่ไม่ได้ห่อหุ้มตัวเอง

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


คุณช่วยยกตัวอย่างเกี่ยวกับการห่อหุ้มสิ่งที่ผิดได้ไหม นั่นอาจจะน่าสนใจ ตัวอย่างของคุณค่อนข้างต่อต้านการขาดการห่อหุ้ม
Gangnus

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

ขอบคุณสำหรับการแก้ไข ฉันพบพิมพ์ผิดเล็กน้อย: "publice" แทนที่จะเป็น "สาธารณะ"
jrh

2

Encapsulation ช่วยให้คุณมีความยืดหยุ่น โดยการแยกโครงสร้างและส่วนต่อประสานช่วยให้คุณสามารถเปลี่ยนโครงสร้างโดยไม่ต้องเปลี่ยนส่วนต่อประสาน

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


ในขณะที่ความคิดที่ดีในทางทฤษฎีโปรดจำไว้ว่าการทำให้ API รุ่นที่ 2 โยนข้อยกเว้นที่เวอร์ชัน 1 ไม่ได้ทำให้ผู้ใช้ไลบรารีหรือคลาสของคุณแย่มาก (และอาจเงียบ!) ฉันสงสัยเล็กน้อยว่าการเปลี่ยนแปลงที่สำคัญใด ๆ สามารถทำ "เบื้องหลัง" ในคลาสข้อมูลเหล่านี้ส่วนใหญ่
jrh

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

@Gangnus คำว่า "ส่วนต่อประสาน" มีความหมายภายนอกคำสำคัญของ Java: dictionary.com/browse/interface
glennsl

@jrh นั่นเป็นปัญหาที่แตกต่างอย่างสิ้นเชิง คุณอาจใช้เพื่อโต้แย้งการห่อหุ้มในบริบทบางอย่างแต่มันก็ไม่ได้ทำให้ข้อโต้แย้งมัน
glennsl

1
การห่อหุ้มแบบเก่าโดยไม่ได้รับจำนวนมาก / การตั้งค่าทำให้เราไม่เพียง แต่ความหลากหลาย แต่ยังความปลอดภัย และชุด / ได้รับการสอนให้เราไปสู่การปฏิบัติที่ไม่ดี
Gangnus

2

ฉันจะพยายามอธิบายปัญหาของการห่อหุ้มและการออกแบบชั้นเรียนและตอบคำถามของคุณในตอนท้าย

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

ไม่ว่าจะเป็นเหตุผลที่มีฟิลด์สาธารณะหรือ getters / setters หรือวิธีการทำธุรกรรมระดับสูงขึ้นหรือการส่งข้อความขึ้นอยู่กับลักษณะของโดเมนที่มีการจำลอง ในหนังสือ Akka Concurrency (ซึ่งฉันสามารถแนะนำได้แม้ว่ามันจะค่อนข้างล้าสมัย) คุณพบตัวอย่างที่แสดงสิ่งนี้ซึ่งฉันจะย่อที่นี่

พิจารณาคลาสผู้ใช้:

public class User {
  private String first = "";
  private String last = "";

  public String getFirstName() {
    return this.first;
  }
  public void setFirstName(String s) {
    this.first = s;
  }

  public String getLastName() {
    return this.last;
  }
  public void setLastName(String s) {
    this.last = s;
  }
}

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

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

System.out.println(user.getFirstName() + " " + user.getLastName());

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

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

เมื่อปรากฎว่าไม่มีวิธีแก้ปัญหาที่สะอาดผ่านการล็อค ทางออกที่สะอาดคือการแค็ปซูลภายในอีกครั้งโดยทำให้พวกเขาหยาบขึ้น:

public class UserName {
   public final String first;
   public final String last;
   public UserName(String first, String last) { ... }
}

public class User
   private UserName name;
   public UserName getName() { return this.name; }
   public setName(UserName n) { this.name = n; }
}

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

วงจรนี้มีความรู้สึกอย่างไร? และตอนนี้มีการห่อหุ้มความรู้สึกในการเขียนโปรแกรมจริง ๆ หรือไม่?

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


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

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

ฉันไม่ได้ใช้ lombok แต่อย่างที่ฉันเข้าใจมันเป็นวิธีการใช้ encapsulation ในระดับที่เฉพาะเจาะจง (getters / setters ในทุกเขต) ด้วยการพิมพ์น้อย มันไม่ได้เปลี่ยนรูปร่างในอุดมคติของ API สำหรับโดเมนปัญหาของคุณ แต่เป็นเพียงเครื่องมือในการเขียนอย่างรวดเร็วยิ่งขึ้น
Joeri Sebrechts

1

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

ตัวอย่างที่ค่อนข้าง contrived: จุดที่เริ่มต้นจากการเป็นคู่คาร์ทีเซียนด้วยและp.x() p.y()คุณทำการปรับใช้ใหม่หรือคลาสย่อยที่ใช้พิกัดเชิงขั้วดังนั้นคุณจึงสามารถโทรp.r()และp.theta()แต่รหัสลูกค้าของคุณที่โทรp.x()และp.y()ยังใช้งานได้ ชั้นเองโปร่งใสแปลงจากรูปแบบเชิงขั้วภายในนั่นคือจะตอนนี้y() return r * sin(theta);(ในตัวอย่างนี้การตั้งค่าเท่านั้นx()หรือy()ไม่สมเหตุสมผลเท่าที่ควร แต่ยังเป็นไปได้)

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


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

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

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

วิธีนั้นมีประสิทธิภาพมากกว่าใช่มั้ย :)
Davislor

0

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

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

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

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

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

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

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

สิ่งนี้ทำให้ชัดเจนขึ้นหรือไม่


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

0

ฉันไม่ใช่นักพัฒนา Java จริงๆ แต่ต่อไปนี้เป็นแพลตฟอร์มที่ไม่เชื่อเรื่องพระเจ้ามาก

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

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

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

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


ยินดีต้อนรับสู่โลก Java (C # เช่นกัน) ถอนหายใจ * แต่สำหรับการใช้ get / set สำหรับการซ่อนการเป็นตัวแทนภายในให้ระวังด้วย มันเกี่ยวกับรูปแบบภายนอกเท่านั้นมันก็โอเค แต่ถ้ามันเกี่ยวกับคณิตศาสตร์หรือแย่กว่านั้นฟิสิกส์หรือของจริงอื่น ๆ การเป็นตัวแทนภายในที่แตกต่างกันจะมีคุณสมบัติต่างกัน กล่าวถึงความคิดเห็นของฉันต่อsoftwareengineering.stackexchange.com/a/358662/44104
Gangnus

@Gangnus: ตัวอย่างประเด็นนั้นโชคร้ายที่สุด เรามีจำนวนไม่มากนัก แต่การคำนวณนั้นแน่นอนเสมอ
Joshua

-1

Getters และ setters เป็นมาตรการ "ในกรณีที่" เพิ่มเพื่อหลีกเลี่ยงการเปลี่ยนโครงสร้างในอนาคตหากโครงสร้างภายในหรือข้อกำหนดการเข้าถึงเปลี่ยนแปลงในระหว่างกระบวนการพัฒนา

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

ตอนนี้ปัญหาเกี่ยวกับภาษา "โบราณ" เช่น Java ก็คือพวกเขาสนับสนุนให้ใช้วิธีการเพื่อจุดประสงค์นี้ซึ่งทำให้โค้ดของคุณละเอียดยิ่งขึ้นอย่างไม่มีที่สิ้นสุด มันยากที่จะเขียนและอ่านยากซึ่งเป็นสาเหตุที่เราใช้ IDE ซึ่งช่วยลดปัญหานี้ไม่ทางใดก็ทางหนึ่ง IDE ส่วนใหญ่จะสร้างตัวเชื่อมต่อและตัวตั้งค่าให้คุณโดยอัตโนมัติและซ่อนไว้เว้นแต่จะแจ้งเป็นอย่างอื่น ลอมบอกเป็นขั้นตอนต่อไปและสร้างพวกเขาเป็นขั้นตอนในช่วงเวลารวบรวมเพื่อให้รหัสของคุณเป็นพิเศษ อย่างไรก็ตามภาษาอื่น ๆ ที่ทันสมัยกว่าได้แก้ไขปัญหานี้ไม่ทางใดก็ทางหนึ่ง ภาษา Scala หรือ. NET เช่น

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

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


ในภาษา "สมัยใหม่" เหล่านั้น API ดูเหมือนการเข้าถึงฟิลด์foo.barแต่อาจได้รับการจัดการโดยการเรียกเมธอด คุณอ้างว่านี้จะดีกว่าวิธีที่ "โบราณ" ของการมี API มีลักษณะเหมือนfoo.getBar()สายวิธี ดูเหมือนว่าเราจะเห็นด้วยว่าเขตข้อมูลสาธารณะนั้นมีปัญหา แต่ฉันอ้างว่าทางเลือก "โบราณ" นั้นยอดเยี่ยมกว่า "สมัยใหม่" หนึ่งเนื่องจาก API ทั้งหมดของเรานั้นมีความสมมาตร ในวิธีการ "สมัยใหม่" เราต้องตัดสินใจว่าสิ่งใดควรเป็นคุณสมบัติและควรเป็นวิธีการที่ซับซ้อนทุกอย่าง (โดยเฉพาะถ้าเราใช้การสะท้อน!)
Warbo

1
1. การซ่อนฟิสิกส์ไม่ดีเสมอไป ดูความคิดเห็นของฉันที่จะsoftwareengineering.stackexchange.com/a/358662/44104 2. ฉันไม่ได้พูดถึงว่าการสร้าง getter / setter นั้นยากหรือง่ายเพียงใด C # มีปัญหาเดียวกันกับที่เรากำลังพูดถึง การขาดการใส่ในกล่องเนื่องจากการแก้ปัญหาการโหลดข้อมูลจำนวนมาก 3. ใช่วิธีการแก้ปัญหาอาจขึ้นอยู่กับไวยากรณ์ แต่โปรดด้วยวิธีที่แตกต่างกว่าที่คุณพูดถึง มันแย่มากเรามีหน้าใหญ่ที่เต็มไปด้วยคำอธิบาย 4. โซลูชันไม่จำเป็นต้องยึดตามไวยากรณ์ มันสามารถทำได้ในขีด จำกัด Java
Gangnus

-1

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

ใช่มันขัดแย้งกัน ฉันแรกวิ่งเข้าไปในคุณสมบัติใน Visual Basic ในประสบการณ์ของฉันในภาษาอื่น ๆ ไม่มีการห่อหุ้มทรัพย์สินรอบทุ่ง เพียงฟิลด์สาธารณะส่วนตัวและที่มีการป้องกัน

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

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

เหตุใดเราจึงไม่ใช้วิธีการที่แท้จริง เพราะมันเป็นVisual Basic (และVBScript ) (oooh! aaah!) การเขียนโปรแกรมสำหรับมวลชน (!) และมันเป็นความโกรธทั้งหมด และในที่สุดไอเดียประชาธิปไตยก็ถูกครอบงำ


ใช่คุณสมบัติของ UI มีความหมายเป็นพิเศษพวกเขาเป็นสาธารณะและเป็นตัวแทนของฟิลด์ จุดดี.
Gangnus

"ทำไมเราไม่ / พวกเขาแค่ใช้วิธีการจริง" ในทางเทคนิคแล้วพวกเขาทำใน. Net version คุณสมบัติเป็นน้ำตาลประโยคสำหรับรับและกำหนดฟังก์ชั่น
Pharap

"idiotocracy" ? คุณไม่ได้หมายถึง " idiosyncrasy " ใช่ไหม
Peter Mortensen

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