การแยกคลาสส่วนใหญ่ออกเป็นฟิลด์ข้อมูลเฉพาะคลาสและเมธอดเท่านั้นคลาส (ถ้าเป็นไปได้) แบบดีหรือแบบต่อต้านหรือไม่?


10

ตัวอย่างเช่นคลาสมักจะมีสมาชิกชั้นเรียนและวิธีการเช่น:

public class Cat{
    private String name;
    private int weight;
    private Image image;

    public void printInfo(){
        System.out.println("Name:"+this.name+",weight:"+this.weight);
    }

    public void draw(){
        //some draw code which uses this.image
    }
}

แต่หลังจากอ่านเกี่ยวกับหลักการความรับผิดชอบเดี่ยวและหลักการเปิดแบบปิดฉันชอบแยกคลาสออกเป็น DTO และคลาสตัวช่วยด้วยวิธีสแตติกเท่านั้นเช่น:

public class CatData{
    public String name;
    public int weight;
    public Image image;
}

public class CatMethods{
    public static void printInfo(Cat cat){
        System.out.println("Name:"+cat.name+",weight:"+cat.weight);
    }

    public static void draw(Cat cat){
        //some draw code which uses cat.image
    }
}

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

คำถามของฉันคือมันเป็นสิ่งที่ดีหรือต่อต้านรูปแบบ?


15
ทำอะไรที่สุ่มสี่สุ่มห้าทุกที่เพราะคุณได้เรียนรู้แนวคิดใหม่ ๆ อยู่เสมอการต่อต้านแบบ
Kayaman

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

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

1
@ user949300 ถ้าคลาสมีหน้าที่รับผิดชอบในฟิลด์เหล่านั้นดังนั้นการย้ายไปไว้ที่อื่นในแอพจะมีผลกระทบเป็นศูนย์ หรือพูดอีกอย่างคือคุณเข้าใจผิด: พวกเขา 100% เป็นผู้รับผิดชอบ
David Arno

2
@DavidArno และ R Schmitz: เขตข้อมูลที่มีอยู่ไม่ถือเป็นความรับผิดชอบสำหรับวัตถุประสงค์ของ SRP ถ้าเป็นเช่นนั้นอาจไม่มี OOP เนื่องจากทุกอย่างจะเป็น DTO จากนั้นคลาสที่แยกต่างหากจะมีโพรซีเดอร์เพื่อทำงานกับข้อมูล ความรับผิดชอบเดียวเกี่ยวข้องกับผู้มีส่วนได้เสียเดียว (แม้ว่าสิ่งนี้ดูเหมือนจะเปลี่ยนไปเล็กน้อยในแต่ละรอบของเงินต้น)
949300

คำตอบ:


10

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

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

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

public class Cat{
    private String name;
    private int weight;
    private Image image;

    public String getInfo(){
        return "Name:"+this.name+",weight:"+this.weight;
    }
    public Image getImage(){
        return image;
    }
}

ตอนนี้Catวัตถุมีตรรกะเพียงพอที่จะให้โค้ดโดยรอบนำไปปฏิบัติได้ง่ายprintInfoและdrawโดยไม่ต้องเปิดเผยคุณลักษณะทั้งหมดในที่สาธารณะ สถานที่ที่เหมาะสมสำหรับสองวิธีนี้อาจไม่ใช่คลาสเทพเจ้าCatMethods(ตั้งแต่printInfoและdrawอาจเป็นข้อกังวลที่แตกต่างกันมากที่สุดดังนั้นฉันคิดว่ามันไม่น่าเป็นไปได้ที่พวกเขาจะอยู่ในชั้นเดียวกัน)

ฉันสามารถจินตนาการสิ่งCatDrawingControllerที่ใช้draw(และอาจใช้การฉีดพึ่งพาสำหรับการรับวัตถุ Canvas) ฉันสามารถจินตนาการอีกคลาสที่ใช้คอนโซลเอาต์พุตบางตัวและใช้งานgetInfo(ดังนั้นprintInfoอาจล้าสมัยในบริบทนี้) แต่สำหรับการตัดสินใจอย่างมีเหตุผลในเรื่องนี้เราต้องรู้บริบทและวิธีการใช้Catชั้นเรียนจริง

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

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


ทริกเกอร์ผู้มีความสุขที่ทริกเกอร์นี้น่าเบื่อ หากคุณมีนักวิจารณ์โปรดแจ้งให้เราทราบ ฉันยินดีที่จะชี้แจงความเข้าใจผิด ๆ หากคุณมี
Doc Brown

ในขณะที่ฉันเห็นด้วยกับคำตอบของคุณโดยทั่วไป ... ฉันรู้สึกว่าgetInfo()อาจมีคนเข้าใจผิดว่ามีคนถามคำถามนี้ มันผสมผสานความรับผิดชอบในการนำเสนออย่างละเอียดเช่นเดียวกับการprintInfo()เอาชนะจุดประสงค์ของการDrawingControllerเรียน
Adriano Repetti

@AdrianoRepetti DrawingControllerผมไม่เห็นว่ามันเอาชนะวัตถุประสงค์ของ ฉันเห็นด้วยgetInfo()และprintInfo()เป็นชื่อที่น่ากลัว พิจารณาtoString()และprintTo(Stream stream)
candied_orange

@candid มันไม่ใช่ (แค่) เกี่ยวกับชื่อ แต่เกี่ยวกับสิ่งที่มันทำ รหัสนั้นเป็นเพียงเกี่ยวกับรายละเอียดงานนำเสนอและเราเพียงแค่กำจัดเอาท์พุทไม่ใช่เนื้อหาที่ประมวลผลและตรรกะ
Adriano Repetti

@AdrianoRepetti: โดยสุจริตนี่เป็นเพียงตัวอย่างที่ออกแบบมาเพื่อให้พอดีกับคำถามต้นฉบับและแสดงให้เห็นถึงจุดของฉัน ในระบบจริงจะมีบริบทโดยรอบซึ่งจะช่วยให้การตัดสินใจเกี่ยวกับวิธีการและชื่อที่ดีขึ้นแน่นอน
Doc Brown

6

ตอบช้า แต่ฉันทนไม่ได้

X คลาสส่วนใหญ่ใน Y ดีหรือต่อต้านแบบ?

ในกรณีส่วนใหญ่กฎส่วนใหญ่ที่นำไปใช้โดยไม่ได้คิดจะผิดอย่างมาก (รวมถึงกฎนี้)

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

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

ตอนนี้ในขณะที่ทำสิ่งนี้ฉันได้สร้างวิธีการคงที่เพื่อทำการสับ นักศึกษาฝึกงานของฉันสร้างคลาส DTO ที่เหมือนคุณCatDataมาก

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

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

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

ฉันถามอย่างไม่เต็มใจ "คุณจะพิจารณาวัตถุไหม"

เขามองกลับไปที่ DTO ของเขาและทำให้ใบหน้าของเขาสับสน "มันเป็นวัตถุ"

"ฉันหมายถึงวัตถุจริง"

"ฮะ?"

"ฉันจะแสดงให้คุณดูคุณตัดสินใจว่ามันมีประโยชน์หรือไม่"

ฉันเลือกชื่อใหม่และทำสิ่งที่ดูคล้าย ๆ อย่างรวดเร็ว:

public class Cat{
    CatData(string catPage) {
        this.catPage = catPage
    }
    private readonly string catPage;

    public string name() { return chop("name prefix", "name suffix"); }
    public string weight() { return chop("weight prefix", "weight suffix"); }
    public string image() { return chop("image prefix", "image suffix"); }

    private string chop(string prefix, string suffix) {
        int start = catPage.indexOf(prefix) + prefix.Length;
        int end = catPage.indexOf(suffix);
        int length = end - start;
        return catPage.Substring(start, length);
    }
}

นี่ไม่ได้ทำสิ่งใดที่คงที่ไม่ได้ทำ แต่ตอนนี้ฉันดูด 14 วิธีการแบบคงที่เข้าไปในชั้นเรียนที่พวกเขาสามารถอยู่คนเดียวกับข้อมูลที่พวกเขาทำงาน

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

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

ฉันกำลังบอกว่าคุณควรติดอยู่กับวัตถุ OO มากกว่าของ DTO หรือไม่? ไม่เงางามของ DTO เมื่อคุณต้องข้ามเขตแดนซึ่งทำให้คุณไม่สามารถเคลื่อนที่ได้ DTO มีสถานที่ของพวกเขา

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


เนื่องจากคำตอบของฉันมีมานานแล้วอย่างน่าขันขอให้ฉันทำให้คุณเข้าใจผิดเกี่ยวกับการตรวจสอบรหัสของคุณ

ตัวอย่างเช่นคลาสมักจะมีสมาชิกชั้นเรียนและวิธีการเช่น:

public class Cat{
    private String name;
    private int weight;
    private Image image;

    public void printInfo(){
        System.out.println("Name:"+this.name+",weight:"+this.weight);
    }

    public void draw(){
        //some draw code which uses this.image
    }
}

ตัวสร้างของคุณอยู่ที่ไหน สิ่งนี้ไม่ได้แสดงให้ฉันรู้ว่ามันมีประโยชน์

แต่หลังจากอ่านเกี่ยวกับหลักการความรับผิดชอบเดี่ยวและหลักการเปิดแบบปิดฉันชอบแยกคลาสออกเป็น DTO และคลาสตัวช่วยด้วยวิธีสแตติกเท่านั้นเช่น:

public class CatData{
    public String name;
    public int weight;
    public Image image;
}

public class CatMethods{
    public static void printInfo(Cat cat){
        System.out.println("Name:"+cat.name+",weight:"+cat.weight);
    }

    public static void draw(Cat cat){
        //some draw code which uses cat.image
    }
}

ฉันคิดว่ามันเหมาะกับหลักการความรับผิดชอบเดี่ยวเพราะตอนนี้ความรับผิดชอบของ CatData คือการเก็บข้อมูลเท่านั้นไม่สนใจวิธีการ (เช่นสำหรับ CatMethods)

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

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

และมันยังเหมาะกับหลักการแบบเปิดเพราะการเพิ่มวิธีการใหม่ไม่จำเป็นต้องเปลี่ยนคลาส CatData

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

คำถามของฉันคือมันเป็นสิ่งที่ดีหรือต่อต้านรูปแบบ?

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

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

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


2

คุณพบหัวข้อการถกเถียงที่สร้างข้อโต้แย้งระหว่างผู้พัฒนามานานกว่าทศวรรษ ในปี 2003 มาร์ตินฟาวเลอร์ได้สร้างวลี "Anemic Domain Model" (ADM)เพื่ออธิบายการแยกข้อมูลและการทำงานนี้ เขา - และคนอื่น ๆ ที่เห็นด้วยกับเขา - ยืนยันว่า "โมเดลโดเมนที่สมบูรณ์" (การผสมข้อมูลและฟังก์ชันการทำงาน) คือ "OO ที่เหมาะสม" และวิธีการ ADM เป็นรูปแบบการต่อต้านที่ไม่ใช่ OO

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

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

ดังนั้นคุณสามารถมีอินเทอร์เฟซ:

public interface CatMethods {
    void printInfo(Cat cat);
    void draw(Cat cat);
}

และการใช้งานที่ได้รับการฉีดเข้าไปในส่วนที่เหลือของระบบของคุณตอนรันไทม์ (ด้วยการใช้งานอื่น ๆ ที่ใช้สำหรับการทดสอบ):

internal class CatMethodsForScreen implements CatMethods {
    public void printInfo(Cat cat) {
        System.out.println("Name:"+cat.name+",weight:"+cat.weight);
    }

    public void draw(Cat cat) {
        //some draw code which uses cat.image
    }
}

หรือเพิ่มพารามิเตอร์พิเศษเพื่อลบผลข้างเคียงจากวิธีการเหล่านี้:

public static class CatMethods {
    public static void printInfo(Cat cat, OutputHandler output) {
        output.println("Name:"+cat.name+",weight:"+cat.weight);
    }

    public static void draw(Cat cat, Canvas canvas) {
        //some draw code which uses cat.image and draws it on canvas
    }
}

แต่ทำไมคุณลองใช้ภาษา OO อย่าง Java เป็นภาษาที่ใช้งานได้แทนที่จะเป็นภาษาที่ใช้งานได้ตั้งแต่ต้น "ฉันเกลียดชวา Java แต่ทุกคนใช้มันฉันก็ต้องทำ แต่อย่างน้อยฉันก็จะเขียนมันในแบบของฉันเอง" หากคุณไม่ได้อ้างว่ากระบวนทัศน์ของ OO นั้นล้าสมัยและล้าสมัยในทางใดทางหนึ่ง ฉันสมัครสมาชิก "ถ้าคุณต้องการ X คุณรู้ว่าจะไปได้ที่ไหน" โรงเรียนแห่งความคิด
Kayaman

@Kayaman " ฉันสมัคร" ถ้าคุณต้องการ X คุณรู้ว่าจะไปที่ไหน "โรงเรียนแห่งความคิด " ฉันไม่. ฉันพบว่าทัศนคตินั้นเป็นทั้งสายตาสั้นและไม่พอใจเล็กน้อย ฉันไม่ได้ใช้ Java แต่ฉันใช้ C # และฉันไม่สนใจคุณสมบัติ "OO ของจริง" ฉันไม่สนใจมรดกวัตถุที่เป็นรัฐและชอบและฉันเขียนวิธีคงที่จำนวนมากและชั้นเรียน (สุดท้าย) ปิดผนึก เครื่องมือและขนาดชุมชนรอบ C # ไม่เป็นสองรองใคร สำหรับ F # มันเป็นวิธีที่ยากจนกว่า ดังนั้นฉันสมัครเป็นสมาชิก "ถ้าคุณต้องการ X ขอให้เพิ่มเข้าไปใน tooling ปัจจุบันของคุณ" โรงเรียนแห่งความคิด
David Arno

1
ฉันจะบอกว่าการเพิกเฉยต่อแง่มุมของภาษานั้นแตกต่างจากการพยายามผสมผสานและจับคู่คุณสมบัติจากภาษาอื่น ๆ ฉันไม่ได้พยายามเขียน "OO ของจริง" เช่นเดียวกับที่พยายามทำตามกฎ (หรือหลักการ) ในวิทยาศาสตร์ที่ไม่แน่นอนเช่นการเขียนโปรแกรมไม่ทำงาน แน่นอนคุณต้องรู้กฎเพื่อให้สามารถทำลายได้ การขาดคุณสมบัติในการสุ่มสี่สุ่มห้าอาจไม่ดีต่อการพัฒนาภาษา ไม่มีความผิด แต่ IMHO ส่วนหนึ่งของฉาก FP ดูเหมือนว่าจะรู้สึกว่า "FP เป็นสิ่งที่ยิ่งใหญ่ที่สุดนับตั้งแต่ขนมปังหั่นบาง ๆ ทำไมผู้คนถึงไม่เข้าใจ" ฉันไม่เคยพูด (หรือคิดว่า) OO หรือ Java คือ "ดีที่สุด"
Kayaman

1
@ Kayaman แต่ "พยายามผสมและจับคู่คุณสมบัติจากภาษาอื่น ๆ " ควรจะหมายถึงอะไร คุณกำลังถกเถียงกันว่าฟีเจอร์ที่ใช้งานได้นั้นไม่ควรถูกเพิ่มลงในจาวาเพราะ "Java เป็นภาษา OO" หรือไม่? ถ้าเป็นเช่นนั้นนั่นคือการโต้เถียงในสายตาของฉัน ปล่อยให้ภาษาพัฒนาไปพร้อมกับความต้องการของนักพัฒนา การบังคับให้พวกเขาเปลี่ยนไปใช้ภาษาอื่นแทนเป็นวิธีที่ผิดฉันรู้สึก แน่นอนว่า "ผู้สนับสนุน FP" ไปด้านบนสุดเกี่ยวกับ FP ว่าเป็นสิ่งที่ดีที่สุดตลอดกาล และบางคน "ผู้สนับสนุน OO" ขึ้นไปด้านบนเกี่ยวกับ OO ที่มี "ชนะการต่อสู้เพราะมันยอดเยี่ยม" ไม่สนใจพวกเขาทั้งสอง
David Arno

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

0

DTOs - วัตถุการขนส่งข้อมูล

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

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


-1

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

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

นี่คือคำถามสองสามข้อที่คุณสามารถถามตัวเองเกี่ยวกับการสมัคร:

  • ฉันจะต้องให้วิธีการอื่นในการเรนเดอร์ภาพ Cat หรือไม่? ถ้าเป็นเช่นนั้นมรดกจะไม่เป็นวิธีที่สะดวกในการทำเช่นนั้น?
  • คลาส Cat ของฉันต้องรับช่วงต่อจากคลาสอื่นหรือไม่

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


-1

มันเป็นรูปแบบที่ดีหรือไม่?

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


OOP "จริง"

เท่าที่ฉันทราบมีการตีความ OOP 2 ครั้ง สำหรับคนส่วนใหญ่มันเดือดลงไปที่ "วัตถุคือชั้นเรียน"

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

ผู้ถือข้อมูล

คุณCatเป็นตัวอย่างสำคัญของผู้ถือข้อมูล แน่นอนว่าด้านนอกของโปรแกรมของคุณแมวเป็นสิ่งที่ทำอะไรบางอย่างแต่ภายในโปรแกรมของคุณCatเป็น String name, เป็น int และภาพweightimage

ผู้ถือข้อมูลนั้นไม่ได้ทำอะไรเลยไม่ได้หมายความว่าพวกเขาไม่มีตรรกะทางธุรกิจ! ถ้าCatชั้นจะมีผู้สร้างที่กำหนดname, weightและimageคุณได้ประสบความสำเร็จในการห่อหุ้มกฎทางธุรกิจที่มีแมวทุกเหล่านั้น หากคุณสามารถเปลี่ยนได้weightหลังจากสร้างCatคลาสแล้วนั่นเป็นกฎทางธุรกิจอีกฉบับหนึ่ง สิ่งเหล่านี้เป็นสิ่งพื้นฐานมาก แต่นั่นก็หมายความว่ามันสำคัญมากที่จะไม่ทำให้พวกเขาผิด - และด้วยวิธีนี้คุณมั่นใจได้ว่าเป็นไปไม่ได้ที่จะผิด

วัตถุ

วัตถุทำอะไรบางอย่าง หากคุณต้องการวัตถุClean * เราสามารถเปลี่ยนเป็น "Objects do one do "

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

ภายในวัตถุนี้จะมีการอ้างอิงถึงสิ่งที่มันต้องการเพื่อตอบสนองความรับผิดชอบเดียวของการเข้าสู่ระบบแมว ในกรณีนี้ว่าเป็นเพียงการอ้างอิงถึงSystemสำหรับSystem.out.printlnแต่ในกรณีส่วนใหญ่ก็จะมีการอ้างอิงส่วนตัวถึงวัตถุอื่น ๆ ถ้าตรรกะการจัดรูปแบบการส่งออกก่อนที่จะพิมพ์มันจะกลายเป็นความซับซ้อนมากเกินไปก็สามารถได้รับการอ้างอิงไปยังวัตถุใหม่CatInfoLogger CatInfoFormatterหากภายหลังบันทึกทุกอย่างจะต้องถูกเขียนไปยังไฟล์คุณสามารถเพิ่มCatToFileLoggerวัตถุที่ทำเช่นนั้น

ผู้ถือข้อมูลเทียบกับวัตถุ - สรุป

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

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

Overkill?

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

อย่างไรก็ตามซอฟต์แวร์ "จริงจัง" (= มากกว่าสคริปต์หรือ 2) อาจมีขนาดใหญ่พอที่จะได้รับประโยชน์จากโครงสร้างเช่นนี้


ตอบ

การแยกคลาสส่วนใหญ่ออกเป็น DTO และคลาสตัวช่วย [... ] ดีหรือแบบต่อต้านหรือไม่?

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

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


* นั่นคือ "สะอาด" ด้วยตัวอักษร C เหมือนกับชื่อหนังสือเพราะ - จำวรรคแรก - นี่เป็นโรงเรียนแห่งความคิดหนึ่งแห่งในขณะที่ยังมีอีกหลายแห่ง

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