คุณจะหลีกเลี่ยง getters และ setters ได้อย่างไร


85

ฉันมีช่วงเวลาที่ยากลำบากในการออกแบบชั้นเรียนในแบบที่เป็นจริง ฉันอ่านแล้วว่าวัตถุแสดงพฤติกรรมของพวกเขาไม่ใช่ข้อมูล ดังนั้นแทนที่จะใช้ getter / setters เพื่อแก้ไขข้อมูลวิธีการของคลาสที่กำหนดควรเป็น "คำกริยา" หรือการดำเนินการกับวัตถุ ยกตัวอย่างเช่นใน 'บัญชี' วัตถุเราจะมีวิธีการWithdraw()และDeposit()มากกว่าsetAmount()ฯลฯ ดู: ทำไมทะเยอทะยานและหมาวิธีเป็นความชั่วร้าย

ตัวอย่างเช่นกำหนดคลาสลูกค้าที่เก็บข้อมูลจำนวนมากเกี่ยวกับลูกค้าเช่นชื่อ DOB, Tel, ที่อยู่เป็นต้นเราจะหลีกเลี่ยง getter / setters เพื่อรับและตั้งค่าคุณลักษณะเหล่านั้นได้อย่างไร วิธีการพิมพ์ 'พฤติกรรม' ประเภทใดที่สามารถเขียนเพื่อเติมข้อมูลทั้งหมดได้


3
มีความเป็นไปได้ที่ซ้ำกันของเหตุผลในการใช้คลาส "old old data" หรือไม่?
ริ้น

3
ดูเพิ่มเติมที่: Getters and Setters justified เมื่อไหร่
gnat

8
ฉันจะชี้ให้เห็นว่าถ้าคุณต้องจัดการกับข้อกำหนดของ Java Beansคุณจะมี getters และ setters หลายสิ่งหลายอย่างใช้ Java Beans (Expression Language ใน jsps) และพยายามหลีกเลี่ยงสิ่งเหล่านี้จะเป็น ... ความท้าทาย

4
... และจากมุมมองตรงกันข้ามจาก MichaelT: หากคุณไม่ได้ใช้ JavaBeans spec (และฉันคิดว่าคุณควรหลีกเลี่ยงมันเว้นแต่ว่าคุณใช้วัตถุในบริบทที่จำเป็น) จากนั้นไม่จำเป็นต้องใช้ หูด "รับ" บนตัวรับโดยเฉพาะอย่างยิ่งสำหรับคุณสมบัติที่ไม่มีตัวตั้งค่าที่เกี่ยวข้อง ผมคิดว่าวิธีการที่เรียกว่าname()บนเป็นที่ชัดเจนหรือชัดเจนกว่าวิธีที่เรียกว่าCustomer getName()
Daniel Pryden

2
@Daniel Pryden: name () อาจหมายถึงทั้งการตั้งค่าหรือรับ ...
IntelliData

คำตอบ:


55

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

ยกตัวอย่างเช่นสมมติว่าโปรเจคของคุณเป็นเกมกระดานเช่น Chess หรือ Battleship คุณอาจมีหลายวิธีในการแสดงสิ่งนี้ในเลเยอร์การนำเสนอ (แอปคอนโซลบริการเว็บ GUI ฯลฯ ) แต่คุณมีโดเมนหลักด้วย ชั้นหนึ่งที่คุณอาจมีคือCoordinateแทนตำแหน่งบนกระดาน วิธีการ "ชั่วร้าย" ในการเขียนจะเป็น:

public class Coordinate
{
    public int X {get; set;}
    public int Y {get; set;}
}

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

การลบตัวตั้งค่า: ความไม่เข้ากัน

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

public class Coordinate
{
    public int X {get; private set;}
    public int Y {get; private set;}

    public Coordinate(int x, int y)
    {
        X = x;
        Y = y;
    }
}

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

การเอา Getters ส่วนที่ 1: การออกแบบสำหรับพฤติกรรม

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

ดังนั้นสมมติว่าสถานที่แรกที่คุณพบว่าตัวเองต้องการCoordinateสำหรับการตรวจจับการชน: คุณต้องการตรวจสอบว่าสองชิ้นครอบครองพื้นที่เดียวกันบนกระดานหรือไม่ นี่คือวิธี "ความชั่วร้าย" (ตัวสร้างถูกละเว้นเนื่องจากความกะทัดรัด):

public class Piece
{
    public Coordinate Position {get; private set;}
}

public class Coordinate
{
    public int X {get; private set;}
    public int Y {get; private set;}
}

    //...And then, inside some class
    public bool DoPiecesCollide(Piece one, Piece two)
    {
        return one.X == two.X && one.Y == two.Y;
    }

และนี่คือวิธีที่ดี:

public class Piece
{
    private Coordinate _position;
    public bool CollidesWith(Piece other)
    {
        return _position.Equals(other._position);
    }
}

public class Coordinate
{
    private readonly int _x;
    private readonly int _y;
    public bool Equals(Coordinate other)
    {
        return _x == other._x && _y == other._y;
    }
}

( IEquatableการใช้งานย่อเพื่อความเรียบง่าย) โดยการออกแบบสำหรับพฤติกรรมมากกว่าการสร้างแบบจำลองข้อมูลเราจึงสามารถลบผู้ได้รับ

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

การลบ Getters ตอนที่ 2: พฤติกรรมภายนอก

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

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

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

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

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

สำหรับปัญหาของเราการแก้ปัญหาโดยใช้รูปแบบนี้จะเป็น:

public class Coordinate
{
    private readonly int _x;
    private readonly int _y;

    public T Transform<T>(IPositionTransformer<T> transformer)
    {
        return transformer.Transform(_x,_y);
    }
}

public interface IPositionTransformer<T>
{
    T Transform(int x, int y);
}

//This one lives in the presentation layer
public class CoordinateToVectorTransformer : IPositionTransformer<Vector2>
{
    private readonly float _tileWidth;
    private readonly float _tileHeight;
    private readonly Vector2 _topLeft;

    Vector2 Transform(int x, int y)
    {
        return _topLeft + new Vector2(_tileWidth*x + _tileHeight*y);
    }
}

ในขณะที่คุณอาจจะสามารถบอก_xและ_yไม่ได้จริงๆห่อหุ้มใด ๆ เพิ่มเติม เราสามารถแยกมันออกได้โดยสร้างอันIPositionTransformer<Tuple<int,int>>ที่คืนค่าพวกมันโดยตรง ขึ้นอยู่กับรสนิยมคุณอาจรู้สึกว่าสิ่งนี้ทำให้การออกกำลังกายทั้งหมดไม่มีจุดหมาย

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

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

public class CoordinateToVectorTransformer
{
    private Dictionary<Coordinate,Vector2> _coordinatePositions;

    public CoordinateToVectorTransformer(int boardWidth, int boardHeight)
    {
        for(int x=0; x<boardWidth; x++)
        {
            for(int y=0; y<boardWidth; y++)
            {
                _coordinatePositions[new Coordinate(x,y)] = GetPosition(x,y);
            }
        }
    }

    private static Vector2 GetPosition(int x, int y)
    {
        //Some implementation goes here...
    }

    public Vector2 Transform(Coordinate coordinate)
    {
        return _coordinatePositions[coordinate];
    }
}

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

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


ตอบอย่างสวยงาม! ฉันต้องการที่จะยอมรับ แต่ก่อนอื่นความเห็น: 1. ฉันคิดว่า toDTO () ยอดเยี่ยม bcuz คุณไม่สามารถเข้าถึง get / set ได้ซึ่งอนุญาตให้คุณเปลี่ยนฟิลด์ที่กำหนดให้กับ DTO โดยไม่ต้องทำลายโค้ดที่มีอยู่ 2. สมมติว่าลูกค้ามีพฤติกรรมเพียงพอที่จะปรับให้เป็นนิติบุคคล, คุณจะเข้าถึงอุปกรณ์ประกอบฉากในการปรับเปลี่ยนได้อย่างไรเช่นการเปลี่ยนที่อยู่ / โทร ฯลฯ
IntelliData

@IntelliData 1. เมื่อคุณพูดว่า "Change the field" คุณหมายถึงเปลี่ยนคำจำกัดความของคลาสหรือเปลี่ยนแปลงข้อมูล? หลังสามารถหลีกเลี่ยงได้โดยการลบ setters สาธารณะ แต่ปล่อยให้ทะเยอทะยานดังนั้นลักษณะ dto จึงไม่เกี่ยวข้อง อดีตไม่ใช่เหตุผลที่ทำให้ผู้ได้รับสาธารณะนั้น "ชั่ว" จริงๆ ดูที่programmers.stackexchange.com/questions/157526/…เป็นต้น
Ben Aaronson

@IntelliData 2. เป็นการยากที่จะตอบโดยไม่รู้พฤติกรรม แต่อาจคำตอบคือ: คุณจะไม่ Customerคลาสใดที่มีพฤติกรรมที่ต้องสามารถเปลี่ยนหมายเลขโทรศัพท์ได้ บางทีหมายเลขโทรศัพท์ของลูกค้าเปลี่ยนแปลงและฉันต้องยืนยันการเปลี่ยนแปลงนั้นในฐานข้อมูล แต่ไม่มีสิ่งใดที่เป็นความรับผิดชอบของวัตถุโดเมนที่ให้พฤติกรรม นั่นเป็นข้อกังวลเรื่องการเข้าถึงข้อมูลและอาจถูกจัดการด้วย DTO และพื้นที่เก็บข้อมูล
Ben Aaronson

@IntelliData การรักษาCustomerข้อมูลของวัตถุโดเมนค่อนข้างใหม่ (ซิงค์กับ db) เป็นเรื่องของการจัดการวงจรชีวิตของมันซึ่งไม่ได้เป็นความรับผิดชอบของตัวเองและอาจจะจบลงด้วยการอาศัยอยู่ในที่เก็บหรือโรงงานหรือคอนเทนเนอร์ IOC หรือ สิ่งที่ instantiates Customers
Ben Aaronson

2
ฉันจริงๆชอบการออกแบบสำหรับพฤติกรรมแนวคิด สิ่งนี้จะแจ้งพื้นฐาน "โครงสร้างข้อมูล" และช่วยหลีกเลี่ยงคลาสที่ใช้งานง่ายทั่วทั้งโลหิตจางและยากต่อการใช้งาน บวกหนึ่ง.
Radarbob

71

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

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

อ่านเพิ่มเติม
เมื่อมี Getters และ Setters เป็นธรรม


3
เหตุใดโฆษณาทั้งหมดของ 'Getter / Setters' จึงชั่วร้าย
IntelliData

46
เพียงการสังฆราชตามปกติโดยนักพัฒนาซอฟต์แวร์ที่ควรรู้ดีกว่า สำหรับบทความที่คุณเชื่อมโยงผู้เขียนใช้วลี "getters and setters are evil" เพื่อให้คุณได้รับความสนใจ แต่นั่นไม่ได้หมายความว่าข้อความจะเป็นจริงอย่างเป็นหมวดหมู่
Robert Harvey

12
@IntelliData บางคนจะบอกคุณว่าจาวาเองนั้นเป็นสิ่งที่ชั่วร้าย
null

18
@MetaFightsetEvil(null);

4
หลักฐานที่แน่ชัดคือ Evil Incarnate หรือลูกพี่ลูกน้องใกล้
บิชอป

58

เป็นการดีที่จะมีวัตถุที่เปิดเผยข้อมูลมากกว่าพฤติกรรม เราเพิ่งเรียกมันว่า "data object" รูปแบบมีอยู่ภายใต้ชื่อเช่น Data Transfer Object หรือ Value Object หากวัตถุประสงค์ของวัตถุคือการเก็บข้อมูลดังนั้น getters และ setters จะสามารถเข้าถึงข้อมูลได้

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

หากคุณดูที่เนื้อหาของบทความจริง ๆ แล้วมันมีบางจุดที่ถูกต้องมันแค่ยืดจุดที่จะทำให้พาดหัว Click-baity ตัวอย่างเช่นบทความระบุว่ารายละเอียดการใช้งานไม่ควรเปิดเผย นี่คือหลักการของการห่อหุ้มและซ่อนข้อมูลซึ่งเป็นพื้นฐานใน OO อย่างไรก็ตามเมธอด getter ไม่ได้นิยามรายละเอียดการนำไปปฏิบัติ ในกรณีของการที่ลูกค้าข้อมูลวัตถุคุณสมบัติของชื่อ , ที่อยู่ฯลฯ ไม่ได้รายละเอียดการปฏิบัติ แต่วัตถุประสงค์ทั้งหมดของวัตถุและควรเป็นส่วนหนึ่งของอินเตอร์เฟซที่สาธารณะ

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

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


ใน oop ผู้เชี่ยวชาญที่เรียกว่าดูเหมือนจะบอกว่านี่ไม่ใช่กรณี
IntelliData

8
จากนั้นอีกครั้งบางคนได้กล่าวว่า 'ไม่เคยใช้ OOP: harmful.cat-v.org/software/OO_programming
JacquesB

7
FTR ฉันคิดว่า "ผู้เชี่ยวชาญ" มากขึ้นยังคงสอนให้สร้างตัวรับ / setters ที่มีความหมายสำหรับทุกสิ่งมากกว่าที่จะไม่สร้างเลย IMO หลังเป็นคำแนะนำที่เข้าใจผิดน้อย
leftaroundabout

4
@leftaroundabout: ตกลง แต่ฉันขอแนะนำที่อยู่ตรงกลางระหว่าง 'เสมอ' และ 'ไม่เคย' ซึ่ง 'ใช้เมื่อเหมาะสม'
JacquesB

3
ฉันคิดว่าปัญหาคือโปรแกรมเมอร์จำนวนมากเปลี่ยนทุกวัตถุ (หรือวัตถุมากเกินไป) ให้เป็น DTO บางครั้งมีความจำเป็น แต่หลีกเลี่ยงพวกเขาให้มากที่สุดเนื่องจากแยกข้อมูลออกจากพฤติกรรม (สมมติว่าคุณเป็นคนใจบุญ OOPer)
user949300

11

ในการแปลงCustomer-class จากวัตถุข้อมูลเราสามารถถามตัวเองคำถามต่อไปนี้เกี่ยวกับเขตข้อมูล:

เราต้องการใช้ {data field} อย่างไร {data field} ใช้ที่ไหน สามารถและควรย้าย {data field} ไปยังคลาสได้หรือไม่?

เช่น:

  • มีจุดประสงค์Customer.Nameอะไร?

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

    ซึ่งนำไปสู่วิธีการ:

    • Customer.FillInTemplate ( ... )
    • Customer.IsApplicableForMailing ( ... )
  • มีจุดประสงค์Customer.DOBอะไร?

    ตรวจสอบอายุของลูกค้า ส่วนลดสำหรับวันเกิดลูกค้า จดหมาย

    • Customer.IsApplicableForProduct ()
    • Customer.GetPersonalDiscount ()
    • Customer.IsApplicableForMailing ()

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

  • ลูกค้าในบริบทของการAccountทำธุรกรรมและการเงินอาจใช้เพื่อ:

    • ช่วยให้มนุษย์ระบุว่าการโอนเงินของพวกเขาไปยังบุคคลที่ถูกต้อง และ
    • กลุ่มAccounts

    ลูกค้ารายนี้ไม่จำเป็นต้องเหมือนสาขาDOB, FavouriteColour, และบางทีอาจจะไม่ได้TelAddress

  • ลูกค้าในบริบทของผู้ใช้ที่เข้าสู่เว็บไซต์ธนาคาร

    สาขาที่เกี่ยวข้องคือ:

    • FavouriteColourซึ่งอาจมาในรูปแบบของชุดรูปแบบส่วนบุคคล;
    • LanguagePreferencesและ
    • GreetingName

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

    • PersonaliseWebPage (หน้าเทมเพลต);
  • ลูกค้าในบริบทของการตลาดและการส่งจดหมายส่วนบุคคล

    ที่นี่ไม่ได้ขึ้นอยู่กับคุณสมบัติของ dataobject แต่เริ่มจากความรับผิดชอบของวัตถุ เช่น:

    • IsCustomerInterestedInAction (); และ
    • GetPersonalDiscounts ()

    ความจริงที่ว่าวัตถุลูกค้านี้มีFavouriteColourคุณสมบัติและ / หรือAddressทรัพย์สินจะไม่เกี่ยวข้อง: บางทีการใช้งานใช้คุณสมบัติเหล่านี้ แต่มันอาจใช้เทคนิคการเรียนรู้ของเครื่องจักรและใช้การโต้ตอบก่อนหน้านี้กับลูกค้าเพื่อค้นหาว่าผลิตภัณฑ์ใดที่ลูกค้าอาจสนใจ


1. แน่นอนว่าคลาสCustomerและAccountคลาสเป็นตัวอย่างและสำหรับตัวอย่างง่ายๆหรือแบบฝึกหัดการบ้านการแยกลูกค้ารายนี้อาจเกินความจริง แต่ด้วยตัวอย่างของการแยกฉันหวังว่าจะแสดงวิธีการเปลี่ยนวัตถุข้อมูลเป็นวัตถุด้วย ความรับผิดชอบจะทำงาน


4
โหวตขึ้นเพราะคุณตอบคำถามจริง ๆ :) อย่างไรก็ตามก็เห็นได้ชัดว่าวิธีแก้ปัญหาที่เสนอนั้นแย่กว่าแค่มี gettes / setters เช่น FillInTemplate แยกหลักการแยกข้อกังวลออกจากกันอย่างชัดเจน ซึ่งเพิ่งไปแสดงว่าหลักฐานของคำถามมีข้อบกพร่อง
JacquesB

@Kasper van den Berg: และเมื่อคุณมีคุณสมบัติมากมายในลูกค้าตามปกติแล้วคุณจะตั้งค่าอย่างไร
IntelliData

2
@IntelliData ค่าของคุณน่าจะมาจากฐานข้อมูล, XML, ฯลฯ ลูกค้าหรือ CustomerBuilder อ่านจากนั้นตั้งค่าโดยใช้ (เช่นใน Java) การเข้าถึงส่วนตัว / แพ็คเกจ / ชั้นในหรือ (ick) ไม่สมบูรณ์แบบ แต่โดยปกติคุณสามารถหลีกเลี่ยงผู้ตั้งค่าสาธารณะได้ (โปรดดูคำตอบของฉันสำหรับรายละเอียดเพิ่มเติมบางส่วน)
user949300

6
ฉันไม่คิดว่ากลุ่มลูกค้าควรรู้เกี่ยวกับสิ่งเหล่านี้
CodesInChaos

แล้วมีอะไรประมาณCustomer.FavoriteColorนี้
Gabe

8

TL; DR

  • การสร้างแบบจำลองสำหรับพฤติกรรมเป็นสิ่งที่ดี

  • การสร้างแบบจำลองเพื่อ abstractions ที่ดี (!) จะดีกว่า

  • บางครั้งจำเป็นต้องใช้วัตถุข้อมูล


พฤติกรรมและสิ่งที่เป็นนามธรรม

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

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

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

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


ระดับลูกค้า

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

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

ถึงกระนั้นผู้ติดตั้งสามารถ / ควรให้บริการที่ไม่สำคัญ: พวกเขาสามารถตรวจสอบรูปแบบที่ถูกต้องของอีเมล - ที่อยู่, การตรวจสอบที่อยู่ไปรษณีย์ ฯลฯ ในทำนองเดียวกัน "getters" สามารถให้บริการระดับสูงเช่นการให้บริการอีเมลในName <user@server.com>รูปแบบ การใช้ฟิลด์ชื่อและที่อยู่อีเมลที่ฝากไว้หรือระบุที่อยู่ในรูปแบบไปรษณีย์ที่ถูกต้อง ฯลฯ แน่นอนว่าฟังก์ชั่นระดับสูงนี้เหมาะสมกับการใช้งานของคุณเป็นอย่างมาก มันอาจจะ overkill เสร็จสมบูรณ์หรืออาจเรียกร้องให้ชั้นอื่นทำถูกต้อง การเลือกระดับนามธรรมนั้นไม่ใช่เรื่องง่าย


เสียงขวา แต่ผมไม่เห็นด้วยในส่วนของการมีเพศสัมพันธ์ ... ;)
IntelliData

6

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

Customer.Name จะเปลี่ยนไปเมื่อใด

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

DOB จะเปลี่ยนไปเมื่อใด

เฉพาะในการสร้างครั้งแรกหรือในการป้อนข้อมูลผิดพลาด หรือถ้าพวกเขาเป็นนักเบสบอล Domincan :-)

ฟิลด์เหล่านี้ไม่สามารถเข้าถึงได้ด้วยตัวตั้งค่าปกติและปกติ บางทีคุณอาจมีCustomer.initialEntry()วิธีการหรือCustomer.screwedUpHaveToChange()วิธีการที่ต้องการการอนุญาตพิเศษ แต่ไม่มีCustomer.setDOB()วิธีสาธารณะ

โดยปกติแล้วลูกค้าจะถูกอ่านจากฐานข้อมูล REST API บาง XML หรืออะไรก็ตาม มีวิธีการCustomer.readFromDB()หรือถ้าคุณเข้มงวดเกี่ยวกับ SRP / การแยกข้อกังวลคุณจะต้องมีผู้สร้างแยกต่างหากเช่นCustomerPersisterวัตถุที่มีread()วิธีการ ภายในพวกเขากำหนดเขตข้อมูล (ฉันชอบใช้การเข้าถึงแพคเกจหรือระดับชั้นใน, YMMV) แต่อีกครั้งหลีกเลี่ยง setters สาธารณะ

(ภาคผนวกเมื่อคำถามมีการเปลี่ยนแปลงบ้าง ... )

สมมติว่าแอปพลิเคชันของคุณใช้ฐานข้อมูลเชิงสัมพันธ์อย่างมาก มันจะโง่Customer.saveToMYSQL()หรือมีCustomer.readFromMYSQL()วิธีการ ที่สร้างการมีเพศสัมพันธ์ที่ไม่พึงประสงค์ให้เป็นรูปธรรมที่ไม่ได้มาตรฐานและมีแนวโน้มที่จะเปลี่ยนเอนทิตี ตัวอย่างเช่นเมื่อคุณเปลี่ยนสคีมาหรือเปลี่ยนเป็น Postgress หรือ Oracle

อย่างไรก็ตาม IMO มันเป็นที่ยอมรับอย่างสมบูรณ์คู่ของลูกค้าไปยังมาตรฐานนามธรรมResultSet , วัตถุตัวช่วยแยก (ฉันจะเรียกมันว่าCustomerDBHelperซึ่งอาจเป็นคลาสย่อยAbstractMySQLHelper) รู้เกี่ยวกับการเชื่อมต่อที่ซับซ้อนทั้งหมดไปยังฐานข้อมูลของคุณรู้รายละเอียดการเพิ่มประสิทธิภาพที่ซับซ้อนรู้ตารางแบบสอบถามเข้าร่วม ฯลฯ ... (หรือใช้ ORM เช่น Hibernate) เพื่อสร้าง ResultSet วัตถุของคุณพูดถึงResultSetซึ่งเป็นมาตรฐานนามธรรมไม่น่าจะมีการเปลี่ยนแปลง เมื่อคุณเปลี่ยนฐานข้อมูลพื้นฐานหรือเปลี่ยนสคีมาลูกค้าจะไม่เปลี่ยนแปลงแต่CustomerDBHelperทำ หากคุณโชคดีมีเพียง AbstractMySQLHelper ที่การเปลี่ยนแปลงซึ่งทำการเปลี่ยนแปลงโดยอัตโนมัติสำหรับลูกค้าผู้ขายการจัดส่ง ฯลฯ ...

วิธีนี้คุณสามารถ (อาจ) หลีกเลี่ยงหรือลดความต้องการผู้ได้รับและผู้ตั้งค่า

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

สมมุติว่าคุณใช้ XML เป็นจำนวนมาก IMO ก็ปรับคู่ลูกค้าของคุณเพื่อเป็นมาตรฐานที่เป็นนามธรรมเช่นงูหลามxml.etree.ElementTreeหรือ Java org.w3c.dom.Element ลูกค้าได้รับและตั้งค่าตัวเองจากที่ อีกครั้งคุณสามารถ (อาจ) ลดความต้องการผู้ได้รับและผู้ตั้งถิ่นฐาน


คุณจะบอกว่าจะแนะนำให้ใช้ตัวสร้างรูปแบบ?
IntelliData

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

1

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

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

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

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

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

หากภาษาของคุณคือ C # หรือ Java ซึ่งไม่มี "structs" คุณสามารถทำเช่นเดียวกัน แต่ตอนนี้ struct ของคุณเป็นคลาสที่สอง ไม่มีแนวคิดที่แท้จริงของ "ความเป็นเจ้าของ" ของข้อมูลหรือ const-ness ดังนั้นหากคุณให้อินสแตนซ์ของคลาสที่มีข้อมูลของคุณและเป็นสาธารณะทั้งหมดสิ่งที่ได้รับสามารถแก้ไขได้ คุณสามารถ "โคลนนิ่ง" ได้แม้ว่าสิ่งนี้อาจมีราคาแพง หรือคุณอาจทำให้คลาสนี้มีข้อมูลส่วนตัว แต่ใช้ accessors วิธีนี้ทำให้ผู้ใช้ในคลาสของคุณสามารถเข้าถึงข้อมูลได้ แต่มันไม่ได้เป็นอินเทอร์เฟซโดยตรงกับคลาสของคุณและเป็นรายละเอียดในการจัดเก็บข้อมูลของคลาสซึ่งเป็นกรณีใช้งานด้วย


0

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

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

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

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

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

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

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

นอกจากนี้ยังมีข้อโต้แย้งที่คุณสามารถเพิ่มตรรกะ / การตรวจสอบในวิธีการตั้งค่า

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

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

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


6
คุณสมบัติ C # ไม่ได้ใช้การห่อหุ้มอะไรมากไปกว่า getters และ setters ...
IntelliData

1
ข้อได้เปรียบของ [GS] เอตเตอร์คือการที่คุณสามารถทำสิ่งที่คุณมักจะไม่ได้ (ค่าตรวจสอบแจ้งให้ผู้สังเกตการณ์) จำลองทุ่งไม่ใช่ที่มีอยู่ ฯลฯ และส่วนใหญ่ที่คุณสามารถเปลี่ยนได้ในภายหลัง เมื่อใช้ลอมบอกแผ่นสำเร็จรูปก็หายไป: @Getter @Setter class MutablePoint3D {private int x, y, z;}.
maaartinus

1
@maaartinus แน่นอนว่า [gs] etter สามารถทำอะไรก็ได้ที่วิธีการอื่นสามารถทำได้ ซึ่งหมายความว่ารหัสผู้โทรใด ๆ ควรตระหนักถึงค่าใด ๆ ที่พวกเขาตั้งไว้อาจทำให้เกิดข้อยกเว้นเปลี่ยนแปลงหรือส่งการแจ้งเตือนการเปลี่ยนแปลงที่อาจเกิดขึ้น ... หรืออย่างอื่น etter [gs] ที่มากหรือน้อยนั้นไม่ได้ให้การเข้าถึงฟิลด์ แต่กำลังทำโค้ดอาร์อาร์อาร์
Nicolas Bousquet

@IntelliData คุณสมบัติ C # อนุญาตให้ไม่เขียนอักขระเดี่ยวที่ไม่มีประโยชน์ 1 ตัวของสำเร็จรูปสำเร็จรูปและไม่สนใจสิ่งทะเยอทะยาน / ผู้ตั้งค่าทั้งหมด ... นี่เป็นความสำเร็จที่ดีกว่าโครงการ lombok นอกจากนี้สำหรับฉัน POJO ที่มี justters / setter ไม่ได้อยู่ที่นี่เพื่อให้บริการห่อหุ้ม แต่จะเผยแพร่รูปแบบข้อมูลที่มีอิสระในการอ่านหรือเขียนเป็นการแลกเปลี่ยนกับบริการ การห่อหุ้มนั้นตรงข้ามกับข้อกำหนดการออกแบบ
Nicolas Bousquet

ฉันไม่คิดว่าคุณสมบัติจะดีจริงๆ แน่นอนว่าคุณได้บันทึกคำนำหน้าและวงเล็บเช่น 5 ตัวอักษรต่อการโทร แต่ 1 พวกเขาดูเหมือนเขตข้อมูลซึ่งทำให้เกิดความสับสน 2. เป็นสิ่งเพิ่มเติมที่คุณต้องการการสนับสนุนในการไตร่ตรอง ไม่มีเรื่องใหญ่ แต่ไม่มีข้อได้เปรียบอย่างใดอย่างหนึ่ง (เมื่อเทียบกับ Java + ลอมบอก; จาวาบริสุทธิ์ล้วนสูญเสียอย่างชัดเจน)
maaartinus

0

ตัวอย่างเช่นให้คลาสของลูกค้าที่เก็บข้อมูลจำนวนมากเช่นชื่อ, DOB, Tel, ที่อยู่เป็นต้นและวิธีใดที่จะหลีกเลี่ยง getter / setters สำหรับการรับและการตั้งค่าคุณลักษณะเหล่านั้นทั้งหมด วิธีการพิมพ์ 'พฤติกรรม' ประเภทใดที่สามารถเขียนเพื่อเติมข้อมูลทั้งหมดได้

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

อย่าสับสนCustomerเป็นคลาสของวัตถุด้วย 'ลูกค้า' ในฐานะผู้ใช้ / นักแสดงที่ทำงานต่าง ๆ โดยใช้ซอฟต์แวร์ของคุณ

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

จากบทความที่เชื่อมโยงเกี่ยวกับ getters / setters เป็นความชั่วร้าย:

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

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

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

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


ลองสิ่งนี้:

เมื่อคุณหลีกเลี่ยงการใช้คำว่า 'ลูกค้า' มากเกินไปกับความหมายที่ต่างกัน 2 แบบมันควรทำให้แนวคิดง่ายขึ้น

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


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

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

0

ฉันเพิ่ม 2 เซ็นต์ของฉันที่นี่พูดถึงวิธีการพูดวัตถุ SQL

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

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

สุดท้ายผมอยากจะทราบว่าขั้นตอนการค้นพบวัตถุคล้ายกันมากกับการสลายตัวของระบบเป็นระบบย่อย ทั้งสองขึ้นอยู่กับพฤติกรรมไม่ใช่สิ่งอื่นใด

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