โครงสร้างเหมือนวัตถุใน Java


195

มันผิดกับวิธีของจาวาในการสร้างโครงสร้างเหมือนวัตถุหรือไม่

class SomeData1 {
    public int x;
    public int y;
}

ฉันสามารถดูคลาสที่มี accessors และ mutators เป็น Java มากกว่า

class SomeData2 {
    int getX();
    void setX(int x);

    int getY();
    void setY(int y);

    private int x;
    private int y;
}

ชั้นเรียนจากตัวอย่างแรกนั้นใช้งานไม่สะดวก

// a function in a class
public int f(SomeData1 d) {
    return (3 * d.x) / d.y;
}

สิ่งนี้ไม่สะดวก

// a function in a class
public int f(SomeData2 d) {
    return (3 * d.getX()) / d.getY();
}

9
แทนที่จะเป็นเขตข้อมูลที่ไม่แน่นอนสาธารณะพิจารณาเขตข้อมูลที่เปลี่ยนรูปไม่ได้สาธารณะหรือเขตข้อมูลที่ไม่แน่นอนของแพคเกจท้องถิ่น จะดีกว่า IMHO
Peter Lawrey

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

กระแทกแดกดันในขณะที่ OO มีความแข็งแกร่งในแง่ของการห่อหุ้มสิ่งนี้มีราคาที่ต้องจ่ายซีพียูและสตอเรจที่ชาญฉลาด ตัวรวบรวมขยะ (เกือบสมบูรณ์) จะขจัดความจำเป็นในการกังวลเกี่ยวกับเวลาที่ควรทำการเคลียร์การอ้างอิงวัตถุ แนวโน้มปัจจุบันกำลังจะเต็มวงโดยใช้ C-like structs off-heap เหมาะอย่างยิ่งสำหรับโซลูชั่นประเภทแคชการสื่อสารระหว่างกระบวนการการดำเนินงานที่ใช้หน่วยความจำมากขึ้นเร็วขึ้น GCo / h ที่ต่ำกว่าและสามารถได้รับประโยชน์จากการจัดเก็บข้อมูลที่ต่ำลงสำหรับชุดข้อมูลของคุณ หากคุณรู้ว่าคุณกำลังทำอะไรคุณจะไม่ถามคำถามนี้ ... ลองคิดใหม่อีกครั้ง!
user924272

@ user924272: เรื่อง "แนวโน้มปัจจุบันกำลังจะเต็มวงโดยใช้ C-like structs off-heap" ซึ่งคุณจะทำใน Java อย่างไร ??? IMHO นี้เป็นพื้นที่ที่มีการแสดง Java อายุของมัน ...
ToolmakerSteve

@ToolmakerSteve - ฉันเห็นวงกลมแล้ว ฉันไม่ใช่คนเดียว บริษัท อย่างอะซูลร้อนแรงในการเก็บขยะน้อยกว่า Java นั้นเก่า จริง วิศวกรที่มองเห็นจุดอ่อนและทำอะไรกับมันมากกว่าจะคราง? พวกเขาสมควรได้รับความเคารพ! +10 ถึง Azul จากฉัน :-)
user924272

คำตอบ:


62

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

ในทางปฏิบัติฟิลด์ส่วนใหญ่มีตัวเชื่อมต่อและตัวตั้ง ทางออกที่เป็นไปได้จะมีลักษณะเช่นนี้:

public property String foo;   
a->Foo = b->Foo;

อัปเดต: ไม่น่าเป็นไปได้อย่างมากที่การสนับสนุนอสังหาริมทรัพย์จะถูกเพิ่มใน Java 7 หรืออาจจะเคย ภาษา JVM อื่น ๆ เช่น Groovy, Scala และอื่น ๆ รองรับคุณสมบัตินี้ทันที - อเล็กซ์มิลเลอร์


28
ที่เลวร้ายเกินไปผมเช่น C # คุณสมบัติสไตล์ (ซึ่งเสียงเหมือนสิ่งที่คุณกำลังพูดถึง)
จอน Onstott

2
ดังนั้นใช้การโหลดมากเกินไป ... private int _x; โมฆะสาธารณะ x (ค่า int) {_x = ค่า; } public int x () {return _x; }
Gordon

12
ฉันชอบความสามารถในการใช้งาน=ซึ่งในความคิดของฉันทำให้รหัสสะอาดขึ้น
Svish

6
@ T-Bull: เพียงเพราะคุณมีสองตัวxที่มีสองสิ่งที่แตกต่างกันไม่ได้ทำให้มันเป็นความคิดที่ดี IMHO เป็นคำแนะนำที่ไม่ดีเพราะอาจนำไปสู่ความสับสนโดยผู้อ่านของมนุษย์ หลักการพื้นฐาน: อย่าทำให้ผู้อ่านทำสองครั้ง ทำให้ชัดเจนว่าคุณกำลังพูดอะไร - ใช้ชื่ออื่นสำหรับเอนทิตีต่าง ๆ แม้ว่าความแตกต่างเป็นเพียงการขีดเส้นใต้ไว้ล่วงหน้า อย่าพึ่งพาเครื่องหมายวรรคตอนโดยรอบเพื่อแยกความแตกต่างของเอนทิตี
ToolmakerSteve

2
@ToolmakerSteve: "อาจนำไปสู่ความสับสน" เป็นเรื่องธรรมดามากที่สุดและยังคงเป็นจุดอ่อนที่สุดเมื่อมันมาถึงการปกป้องการเข้ารหัสความเชื่อ (เมื่อเทียบกับรูปแบบการเข้ารหัส) มีบางคนที่อาจสับสนกับสิ่งที่ง่ายที่สุดเสมอ คุณมักจะพบว่ามีใครบางคนบ่นว่าเขาทำผิดพลาดหลังจากตื่นนอนและเขียนโค้ดเป็นเวลาครึ่งสัปดาห์หรือบางคนเช่นนั้นแล้วตำหนิรูปแบบการเขียนโค้ดที่ทำให้เข้าใจผิด ฉันจะไม่ปล่อยให้นับ สไตล์นั้นถูกต้องชัดเจนและทำให้เนมสเปซสะอาด นอกจากนี้ยังไม่มีเอนทิตีที่แตกต่างกันที่นี่มี / หนึ่ง / เอนทิตีและมีรหัสสำเร็จรูปบางส่วนที่อยู่รอบ ๆ
T-Bull

290

ปรากฏว่าคน Java จำนวนมากไม่คุ้นเคยกับแนวทางการเข้ารหัสของ Sun Java ซึ่งกล่าวว่าค่อนข้างเหมาะสมที่จะใช้ตัวแปรอินสแตนซ์สาธารณะเมื่อคลาสนั้นเป็น "Struct" เป็นหลักหาก Java รองรับ "struct" (เมื่อไม่มีพฤติกรรม)

คนมักจะคิดว่า getters และ setters เป็นวิธีของ Java ราวกับว่าพวกเขาเป็นหัวใจของ Java ไม่เป็นเช่นนั้น หากคุณปฏิบัติตามแนวทางการเข้ารหัสของ Sun Java โดยใช้ตัวแปรอินสแตนซ์สาธารณะในสถานการณ์ที่เหมาะสมคุณกำลังเขียนโค้ดได้ดีกว่าการถ่วงด้วยตัวรับสัญญาณที่ไม่จำเป็น

Java Code Conventions จากปี 1999และยังคงไม่เปลี่ยนแปลง

10.1 ให้การเข้าถึงอินสแตนซ์และตัวแปรของคลาส

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

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

http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-137265.html#177

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

http://docs.oracle.com/javase/1.3/docs/guide/collections/designfaq.html#28


88
+1 สำหรับการมีแหล่งข้อมูลที่เชื่อถือได้จริง คำตอบอื่น ๆ คือผู้คนปั่นความคิดเห็นของพวกเขาเหมือนพวกเขาเป็นข้อเท็จจริง
ArtOfWarfare

1
มีข้อกำหนดของ Java Beans ซึ่งเป็นวิธีมาตรฐานในการเข้าถึงคุณสมบัติโดยใช้วิธีรับและตั้งค่า ... ดูen.wikipedia.org/wiki/JavaBeansเพื่อดูภาพรวม
user924272

4
@ user924272: Java Beans เป็นข้อมูลจำเพาะที่เกี่ยวข้องกับคำตอบนี้ซึ่งจะอธิบายเมื่อมันเหมาะสมที่จะใช้ "ตัวแปรอินสแตนซ์สาธารณะ"? หากข้อมูลจำเพาะเป็นวิธีมาตรฐานในการเปลี่ยนตัวแปรอินสแตนซ์โดยอัตโนมัติให้เป็นคุณสมบัติ Ala C # อาจเป็นสิ่งที่เกี่ยวข้อง แต่มันไม่ถูกต้องใช่ไหม เพียงระบุการตั้งชื่อของตัวสร้างแผ่นวงจรสำเร็จรูปและตัวตั้งค่าที่จะต้องสร้างเพื่อทำแผนที่ดังกล่าว
ToolmakerSteve

@ToolmakerSteve นี่คือคำถาม java นอกจากนี้คำถามจะช่วยแก้ปัญหาที่พบบ่อยซึ่งมีข้อกำหนดอยู่ จากมุมมองของโรงเรียนเก่ามันง่ายกว่าที่จะทำการดีบักการกลายพันธุ์ของฟิลด์เมื่อมีวิธีมาตรฐานในการทำ - ตั้งค่าเบรกพอยต์ที่ setter นี่อาจจะทำให้ล้าสมัยด้วย debuggers ที่ทันสมัย ​​แต่ฉันถูกบังคับให้ขมวดคิ้วในชั้นเรียนที่โดยตรง "หมัด" วัตถุ ... ในขณะนี้ก็โอเคสำหรับการใช้งานที่มีขนาดเล็กมันเป็นอาการปวดหัวที่แท้จริงสำหรับแอพขนาดใหญ่และองค์กรขนาดใหญ่
user924272

223

ใช้สามัญสำนึกจริงๆ หากคุณมีสิ่งที่ชอบ:

public class ScreenCoord2D{
    public int x;
    public int y;
}

จากนั้นก็มีจุดเล็ก ๆ ในการห่อหุ้มพวกมันด้วยตัวดูด คุณจะไม่เก็บพิกัด x และ y เป็นพิกเซลทั้งหมดด้วยวิธีอื่น Getters และ setters จะทำให้คุณช้าลงเท่านั้น

ในทางกลับกันด้วย:

public class BankAccount{
    public int balance;
}

คุณอาจต้องการเปลี่ยนวิธีการคำนวณยอดคงเหลือ ณ จุดหนึ่งในอนาคต นี่ควรใช้ getters และ setters จริงๆ

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


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

@SpacenJasset: FYI ฉันไม่เห็นตัวอย่างของคุณ (ค่าส่งคืนหลายค่าพิกัดเชิงขั้ว) มีผลต่อการใช้ฟิลด์สาธารณะเทียบกับ getter / setters หรือไม่ ในกรณีที่มีค่าส่งคืนหลายค่าอาจเป็นเคาน์เตอร์ที่มีประสิทธิผลเนื่องจากเนื้อหาที่ผู้โทรควรจะได้รับค่าที่ส่งคืนเท่านั้น สิ่งนี้อาจเป็นจริงสำหรับการส่งคืนพิกัดเชิงขั้วจากวัตถุ (x, y) - พิจารณาข้อผิดพลาดทางคณิตศาสตร์แบบสะสมเนื่องจากการเปลี่ยนแปลงองค์ประกอบแต่ละส่วนของขั้ว coord จะถูกแปลงกลับเป็น (x, y)
ToolmakerSteve

@ SpacenJasset: แต่ฉันเห็นด้วยกับหลักการของคุณ
ToolmakerSteve

1
คุณมีจุดที่ถูกต้อง แต่ฉันรู้สึกว่าสิ่งที่เป็นพิกเซลเป็นตัวอย่างที่ไม่ดีเนื่องจากทำให้แน่ใจว่าพิกเซลอยู่ในหน้าต่าง (เป็นเพียงตัวอย่าง) เป็นสิ่งที่บางคนอาจทำและนอกเหนือจากนั้นทำให้บางคนตั้งค่าพิกเซล(-5, -5)เป็น ความคิดที่ดี. :-)
Horsey

50

ในการแก้ไขปัญหาความไม่แน่นอนคุณสามารถประกาศ x และ y เป็นที่สิ้นสุด ตัวอย่างเช่น:

class Data {
  public final int x;
  public final int y;
  public Data( int x, int y){
    this.x = x;
    this.y = y;
  }
}

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

รหัสลูกค้าสามารถมี 'ความสะดวก' ที่คุณอธิบายไว้ในโพสต์ของคุณ

public class DataTest {
    public DataTest() {
        Data data1 = new Data(1, 5);
        Data data2 = new Data(2, 4);
        System.out.println(f(data1));
        System.out.println(f(data2));
    }

    public int f(Data d) {
        return (3 * d.x) / d.y;
    }

    public static void main(String[] args) {
        DataTest dataTest = new DataTest();
    }
}

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

@ToolmakerSteve - ขอบคุณสำหรับความคิดเห็น - ชื่นชมมาก
Brian

ฉันพยายามที่จะใช้อินสแตนซ์สุดท้ายของชั้นสุดท้ายกับเขตสุดท้ายเป็น struct "กรณี" แต่ในการแสดงออกหมายข้อมูลตัวอย่างเช่นผมได้case Case expressions must be constant expressionsอะไรคือสิ่งที่อยู่รอบ ๆ นี้? แนวคิดเกี่ยวกับสำนวนที่นี่คืออะไร?
n611x007

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

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

11

อย่าใช้publicฟิลด์

อย่าใช้publicฟิลด์เมื่อคุณต้องการห่อพฤติกรรมภายในของชั้นเรียน ใช้java.io.BufferedReaderตัวอย่างเช่น มีฟิลด์ต่อไปนี้:

private boolean skipLF = false; // If the next character is a line feed, skip it

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

ใช้publicฟิลด์

เวลานี้Pointระดับตัวอย่างเช่น:

class Point {
    private double x;
    private double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public double getX() {
        return this.x;
    }

    public double getY() {
        return this.y;
    }

    public void setX(double x) {
        this.x = x;
    }

    public void setY(double y) {
        this.y = y;
    }
}

นี่จะทำให้การคำนวณระยะทางระหว่างจุดสองจุดนั้นเจ็บปวดมากในการเขียน

Point a = new Point(5.0, 4.0);
Point b = new Point(4.0, 9.0);
double distance = Math.sqrt(Math.pow(b.getX() - a.getX(), 2) + Math.pow(b.getY() - a.getY(), 2));

ชั้นไม่มีพฤติกรรมใด ๆ นอกจาก getters และ setters ธรรมดา เป็นที่ยอมรับได้ที่จะใช้เขตข้อมูลสาธารณะเมื่อชั้นแสดงเพียงโครงสร้างข้อมูลและไม่มีและจะไม่มีลักษณะการทำงาน (thin getters และ setters ไม่ถือเป็นพฤติกรรมที่นี่) มันสามารถเขียนได้ดีกว่าด้วยวิธีนี้:

class Point {
    public double x;
    public double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
}

Point a = new Point(5.0, 4.0);
Point b = new Point(4.0, 9.0);
double distance = Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2));

Clean!

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


(นี่คือสิ่งที่คำตอบนี้จะอธิบายในการอ้างอิง"ข้อตกลงรหัสสำหรับภาษาการเขียนโปรแกรม Java: 10. วิธีปฏิบัติด้านการเขียนโปรแกรม" :

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

ดังนั้นเอกสารอย่างเป็นทางการก็ยอมรับการปฏิบัติเช่นนี้ด้วย)


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

public final double x;
public final double y;

8

โดยวิธีการที่โครงสร้างที่คุณกำลังให้เป็นตัวอย่างที่มีอยู่แล้วใน Java java.awt.Pointห้องสมุดชั้นฐานเป็น มันมี x และ y เป็นฟิลด์สาธารณะตรวจสอบได้ด้วยตัวคุณเอง

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


+1 พูดถึงปัญหาได้ดี - วิธีที่ผลลัพธ์ยังไม่เหมือนโครงสร้าง C อย่างไรก็ตามปัญหาที่คุณยกเกี่ยวกับวัตถุจาวามักจะอ้างอิงไม่ได้รับการปรับปรุงโดยการสร้าง setter แทนการมีฟิลด์ที่เขียนได้สาธารณะ (ซึ่งเป็นสาระสำคัญของคำถามของ OP - การเป็นตัวแทนที่จะใช้) แต่มันเป็นข้อโต้แย้งในความโปรดปรานของการทำ NEITHER มันเป็นข้อโต้แย้งสำหรับ IMMUTABILITY ซึ่งสามารถทำได้ทั้งเป็นpublic finalฟิลด์เช่นเดียวกับในคำตอบของไบรอันหรือโดยมีประชาชนทะเยอทะยาน แต่ไม่มีประชาชนเซทเทอร์ นั่นคือไม่ว่าจะใช้เขตข้อมูลหรือตัวเข้าถึงเป็นสาระสำคัญ
ToolmakerSteve

8

Re: aku, izb, John Topley ...

ระวังปัญหาความไม่แน่นอน ...

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

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

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

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

โปรดดูที่: " Java ที่มีประสิทธิภาพ " โดย Joshua Bloch - รายการ # 13: ความไม่สามารถเปลี่ยนใจได้

PS: โปรดทราบว่า JVM ทุกวันนี้จะเพิ่มประสิทธิภาพของ getMethod หากเป็นไปได้ซึ่งจะทำให้เกิดคำสั่งในการอ่านฟิลด์เพียงครั้งเดียว


12
getter / setter แก้ไขปัญหานี้อย่างไร คุณยังคงมีการอ้างอิงคุณไม่มีการประสานกับการดำเนินการใด ๆ Getters / setters ไม่ได้ให้ความคุ้มครองในและของตัวเอง
he_the_great

1
Getters และ setters สามารถทำการซิงโครไนซ์ได้ถ้าต้องการ นอกจากนี้คุณยังคาดหวังว่า getters และ setter จะทำมากกว่าที่ระบุไว้ ปัญหาของการซิงโครไนซ์ยังคงเป็นเรื่องของวิตามิน
user924272

7

ฉันได้ลองทำสิ่งนี้ในสองสามโครงการตามทฤษฎีที่ getters และ setters สร้างความสับสนให้กับโค้ดโดยใช้ cruft ที่ไม่มีความหมายและภาษาอื่น ๆ ดูเหมือนจะใช้ได้ดีกับการซ่อนข้อมูลหรือการแบ่งหน้าที่ความรับผิดชอบ (เช่น python)

ดังที่คนอื่น ๆ ได้กล่าวไว้ข้างต้นมี 2 ปัญหาที่คุณพบและพวกเขาไม่สามารถแก้ไขได้จริง:

  • เครื่องมืออัตโนมัติใด ๆ ในโลก java อาศัยการประชุม getter / setter เช่นเดียวกับที่ระบุไว้โดยผู้อื่นแท็ก jsp การกำหนดค่าสปริงเครื่องมือคราส ฯลฯ ฯลฯ ... การต่อสู้กับสิ่งที่เครื่องมือของคุณคาดว่าจะเห็นเป็นสูตรสำหรับเซสชันที่ยาวนานซึ่งหมุนรอบผ่าน Google พยายามค้นหาวิธีที่ไม่เป็นมาตรฐานในการเริ่มต้น ถั่วฝักยาว ไม่คุ้มกับปัญหาเลย
  • เมื่อคุณมีแอปพลิเคชั่นที่เข้ารหัสอย่างสวยงามพร้อมตัวแปรสาธารณะหลายร้อยตัวคุณจะพบสถานการณ์อย่างน้อยหนึ่งสถานการณ์ที่ไม่เพียงพอ - ซึ่งคุณต้องการความไม่เปลี่ยนแปลงอย่างแน่นอนหรือคุณต้องทริกเกอร์เหตุการณ์เมื่อตั้งค่าตัวแปรหรือคุณต้องการโยน ข้อยกเว้นเกี่ยวกับการเปลี่ยนแปลงตัวแปรเนื่องจากมันตั้งค่าสถานะวัตถุเป็นสิ่งที่ไม่พึงประสงค์ จากนั้นคุณก็ติดอยู่กับตัวเลือกที่ไม่น่าจะเป็นไปได้ระหว่างการทำให้รหัสของคุณยุ่งเหยิงด้วยวิธีพิเศษบางอย่างในทุกที่ที่มีการอ้างอิงตัวแปรโดยตรงโดยมีแบบฟอร์มการเข้าถึงพิเศษสำหรับตัวแปร 3 ใน 1,000 ตัวในใบสมัครของคุณ

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

Java นั้นละเอียดมากและนี่เป็นสิ่งที่น่าดึงดูด อย่าทำมัน


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

4

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

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

บทความ JavaWorld เหตุใดเมธอด Getter และ Setter เป็นบทความที่ไม่ดีอาจเป็นที่สนใจของคุณในการพิจารณาว่าเมื่อใดที่จะไม่ใช้เมธอด accessor และ mutator

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


1
+1 สำหรับลิงก์ไปยังบทความ "Why Getter and Setter Methods is Evil" อย่างไรก็ตามคำตอบของคุณจะชัดเจนขึ้นถ้าคุณชี้ให้เห็นว่าทั้งสองเขตข้อมูลสาธารณะและผู้ทะเยอทะยานสาธารณะ / ผู้ตั้งค่าไม่ใช่วิธี Java: ตามที่อธิบายในบทความนั้น - ถ้าเป็นไปได้อย่าทำอย่างใดอย่างหนึ่ง ให้วิธีการเฉพาะแก่ลูกค้าในสิ่งที่พวกเขาต้องทำแทนการทำมิเรอร์การเป็นตัวแทนภายในของอินสแตนซ์ อย่างไรก็ตามสิ่งนี้ไม่ได้ตอบคำถามที่ถูกถามจริง ๆ (สำหรับกรณี "struct" แบบง่าย) ซึ่งตอบได้ดีกว่าโดย developer.g, izb และ Brian
ToolmakerSteve

3

ไม่มีอะไรผิดปกติกับรหัสประเภทนั้นโดยที่ผู้เขียนรู้ว่าพวกเขาเป็น structs (หรือกระสวยข้อมูล) แทนวัตถุ นักพัฒนา Java จำนวนมากไม่สามารถบอกความแตกต่างระหว่างวัตถุที่มีรูปแบบที่ดี (ไม่ใช่แค่คลาสย่อยของ java.lang.Object แต่เป็นวัตถุจริงในโดเมนที่ระบุ) และสับปะรด ถึงแม้ว่าพวกเขาจะจบลงด้วยการเขียน structs เมื่อพวกเขาต้องการวัตถุและผู้แทน


สับปะรดทำให้ฉันหัวเราะ :)
Guillaume

อย่างไรก็ตามคำตอบนี้ไม่ได้เกี่ยวกับความแตกต่างนั้น การพูดพาดพิงกับนักพัฒนาที่ไม่มีทักษะไม่ได้ช่วยให้นักพัฒนาเหล่านั้นรู้ว่าจะสร้าง struct เมื่อใดและเมื่อใดจึงจะสร้างคลาส
ToolmakerSteve

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

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

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

2

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

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

อย่างไรก็ตามในการยืนยันของ e-bartek นั้นเป็นไปได้ยากมากที่ IMO จะให้การสนับสนุนด้านคุณสมบัติใน Java 7


1
สิ่งนี้เป็นเรื่องจริง แต่ถ้าคุณใช้ Java ไม่ใช่เรื่องที่น่ากังวลเนื่องจากคุณสามารถปรับโครงสร้างรหัสทั้งหมดได้ในคลิกเดียว (Eclipse, Netbeans, VIM และ Emacs ด้วย) หากคุณเลือกที่จะเป็นหนึ่งในเพื่อนแบบไดนามิกเช่น Groovy การห่อหุ้มอย่างง่ายอาจทำให้คุณต้องแก้ไขเป็นเวลาหลายชั่วโมงหรือหลายวัน โชคดีที่คุณมีกรณีทดสอบซึ่งจะแจ้งให้คุณทราบ ... คุณต้องแก้ไขรหัสอีกเท่าไร
Dan Rosenstark

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

2

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

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


2

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

stream.mapToInt(SomeData1::x)

ไม่ถูกกฎหมาย แต่

stream.mapToInt(SomeData2::getX)

คือ.


ในกรณีนี้คุณสามารถใช้งานdata -> data.xได้ซึ่งยังคงสมเหตุสมผล
James Kraus

1

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


1

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


1

ที่นี่ฉันสร้างโปรแกรมเพื่อป้อนชื่อและอายุของ 5 คนที่แตกต่างกันและดำเนินการเรียงลำดับการเลือก (อายุฉลาด) ฉันใช้คลาสที่ทำหน้าที่เป็นโครงสร้าง (เช่นภาษาการเขียนโปรแกรม C) และคลาสหลักเพื่อดำเนินการให้เสร็จสมบูรณ์ ที่นี่ฉันทำรหัส ...

import java.io.*;

class NameList {
    String name;
    int age;
}

class StructNameAge {
    public static void main(String [] args) throws IOException {

        NameList nl[]=new NameList[5]; // Create new radix of the structure NameList into 'nl' object
        NameList temp=new NameList(); // Create a temporary object of the structure

        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));

        /* Enter data into each radix of 'nl' object */

        for(int i=0; i<5; i++) {
            nl[i]=new NameList(); // Assign the structure into each radix

            System.out.print("Name: ");
            nl[i].name=br.readLine();

            System.out.print("Age: ");
            nl[i].age=Integer.parseInt(br.readLine());

            System.out.println();
        }

        /* Perform the sort (Selection Sort Method) */

        for(int i=0; i<4; i++) {
            for(int j=i+1; j<5; j++) {
                if(nl[i].age>nl[j].age) {
                    temp=nl[i];
                    nl[i]=nl[j];
                    nl[j]=temp;
                }
            }
        }

        /* Print each radix stored in 'nl' object */

        for(int i=0; i<5; i++)
            System.out.println(nl[i].name+" ("+nl[i].age+")");
    }
}

รหัสข้างต้นคือข้อผิดพลาดฟรีและทดสอบ ... เพียงคัดลอกและวางลงใน IDE ของคุณและ ... คุณรู้และอะไร ??? :)


0

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


0

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


0

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


0

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

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

ปรัชญา getter / setter กำหนดค่าใช้จ่ายในกรณีง่าย ๆ จำนวนมากซึ่งไม่จำเป็น

ไม่ว่าลักษณะด้านจะสะอาดขึ้นหรือไม่นั้นค่อนข้างมีคุณภาพ ฉันพบว่ามันง่ายที่จะเห็นตัวแปรในชั้นเรียนและดูตรรกะแยกจากกัน ในความเป็นจริง raison d'etre สำหรับการเขียนโปรแกรม Apect-oriented คือความกังวลจำนวนมากถูกตัดขวางและการจัดแบ่งพวกมันในร่างกายคลาสนั้นไม่เหมาะ (การบันทึกเป็นตัวอย่าง - ถ้าคุณต้องการบันทึกทั้งหมดทำให้ Java ต้องการให้คุณ เขียนกลุ่มผู้ได้รับทั้งหมดและทำให้ข้อมูลตรงกัน แต่ AspectJ อนุญาตให้คุณใช้งานแบบหนึ่งครั้ง)

ปัญหาของ IDE คือแฮร์ริ่งแดง มันไม่ได้เป็นเรื่องของการพิมพ์เพราะมันเป็นมลพิษจากการอ่านและการมองเห็นที่เกิดขึ้นจากการรับ / เซต

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

ฉันหวังว่าการรับรู้ของ AspectJ จะป้องกันผู้คนจากการตั้งค่าภาษาแบบไดนามิกก่อนกำหนด

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