Apakah memisahkan sebagian besar kelas menjadi hanya bidang data dan hanya kelas metode (jika mungkin) yang baik atau anti-pola?

10

Sebagai contoh, suatu kelas biasanya memiliki anggota dan metode kelas, misalnya:

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
    }
}

Tetapi setelah membaca tentang prinsip tanggung jawab tunggal dan prinsip tertutup terbuka, saya lebih suka memisahkan kelas menjadi kelas DTO dan pembantu dengan metode statis saja, misalnya:

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
    }
}

Saya pikir ini sesuai dengan prinsip tanggung jawab tunggal karena sekarang tanggung jawab CatData adalah hanya menyimpan data, tidak peduli dengan metode (juga untuk CatMethods). Dan itu juga sesuai dengan prinsip tertutup terbuka karena menambahkan metode baru tidak perlu mengubah kelas CatData.

Pertanyaan saya adalah, apakah ini baik atau anti-pola?

ocomfd
sumber
15
Melakukan sesuatu secara membabi buta di mana-mana karena Anda mempelajari konsep baru selalu merupakan anti-pola.
Kayaman
4
Apa gunanya kelas jika selalu lebih baik memiliki data terpisah dari metode yang memodifikasinya? Kedengarannya seperti pemrograman non-berorientasi objek (yang tentunya memiliki tempatnya). Karena ini ditandai "berorientasi objek" Saya menganggap Anda ingin menggunakan alat yang disediakan OOP?
user1118321
4
Pertanyaan lain yang membuktikan bahwa SRP sangat disalahpahami sehingga harus dilarang. Menyimpan data di banyak bidang publik bukan merupakan tanggung jawab .
user949300
1
@ user949300, jika kelas bertanggung jawab untuk bidang-bidang tersebut, maka memindahkannya ke tempat lain di aplikasi tentu saja tidak akan memiliki pengaruh. Atau dengan kata lain, Anda salah: mereka 100% adalah tanggung jawab.
David Arno
2
@ Davidvido dan R Schmitz: Bidang yang berisi tidak dihitung sebagai tanggung jawab untuk tujuan SRP. Jika ya, mungkin tidak ada OOP, karena semuanya akan menjadi DTO, dan kemudian kelas terpisah akan berisi prosedur untuk mengerjakan data. Tanggung jawab tunggal berkaitan dengan satu pemangku kepentingan tunggal . (Meskipun ini tampaknya sedikit berubah pada setiap iterasi kepala sekolah)
user949300

Jawaban:

10

Anda telah menunjukkan dua ekstrem ("semuanya pribadi dan semua (mungkin tidak terkait) metode dalam satu objek" vs "semuanya publik dan tidak ada metode di dalam objek"). Pemodelan OO yang baik IMHO bukan dari mereka , sweet spot adalah suatu tempat di tengah.

Satu uji lakmus dari metode atau logika apa yang termasuk dalam kelas, dan apa yang termasuk di luar, adalah untuk melihat dependensi yang akan diperkenalkan oleh metode tersebut. Metode yang tidak memperkenalkan dependensi tambahan baik-baik saja, asalkan cocok dengan abstraksi objek yang diberikan . Metode yang memang memerlukan dependensi eksternal tambahan (seperti perpustakaan gambar atau perpustakaan I / O) jarang cocok. Bahkan jika Anda akan membuat dependensi menghilang dengan menggunakan injeksi dependensi, saya masih akan berpikir dua kali jika menempatkan metode seperti itu di dalam kelas domain benar-benar diperlukan.

Jadi Anda tidak boleh membuat setiap anggota menjadi publik, Anda juga tidak perlu menerapkan metode untuk setiap operasi pada objek di dalam kelas. Berikut ini adalah saran alternatif:

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;
    }
}

Sekarang Catobjek menyediakan cukup logika untuk membiarkan kode di sekitarnya dengan mudah diimplementasikan printInfodan draw, tanpa memaparkan semua atribut di depan umum. Tempat yang tepat untuk kedua metode ini kemungkinan besar bukan kelas dewa CatMethods(karena printInfodan drawkemungkinan besar adalah keprihatinan yang berbeda, jadi saya pikir sangat tidak mungkin mereka termasuk dalam kelas yang sama).

Saya bisa membayangkan CatDrawingControlleryang mengimplementasikan draw(dan mungkin menggunakan injeksi ketergantungan untuk mendapatkan objek Kanvas). Saya juga bisa membayangkan kelas lain yang mengimplementasikan beberapa keluaran dan penggunaan konsol getInfo(jadi printInfomungkin menjadi usang dalam konteks ini). Tetapi untuk membuat keputusan yang masuk akal mengenai hal ini, kita perlu mengetahui konteks dan bagaimana Catkelas akan benar-benar digunakan.

Itulah sebenarnya cara saya menafsirkan kritik Model Anemic Domain Fowler - untuk logika yang umumnya dapat digunakan kembali (tanpa ketergantungan eksternal) kelas domain itu sendiri adalah tempat yang baik, sehingga mereka harus digunakan untuk itu. Tetapi itu tidak berarti menerapkan logika apa pun di sana, justru sebaliknya.

Perhatikan juga contoh di atas masih menyisakan ruang untuk membuat keputusan tentang (im) mutabilitas. Jika Catkelas tidak akan mengekspos setter apa pun, dan Imagetidak dapat diubah sendiri, desain ini akan memungkinkan untuk membuat Cattidak berubah (yang tidak akan dilakukan oleh pendekatan DTO). Tetapi jika Anda berpikir ketidakberdayaan tidak diperlukan atau tidak membantu untuk kasus Anda, Anda juga bisa menuju ke sana.

Doc Brown
sumber
Pemicu downvoters bahagia, ini jadi membosankan. Jika Anda memiliki beberapa kritik, beri tahu saya. Saya akan dengan senang hati menjelaskan kesalahpahaman, jika ada.
Doc Brown
Meskipun saya umumnya setuju dengan jawaban Anda ... Saya merasa getInfo()mungkin menyesatkan seseorang yang mengajukan pertanyaan ini. Ini secara halus mencampur tanggung jawab presentasi seperti printInfo()halnya, mengalahkan tujuan DrawingControllerkelas
Adriano Repetti
@AdrianoRepetti Saya tidak melihat itu mengalahkan tujuan DrawingController. Saya setuju itu getInfo()dan printInfo()nama-nama yang mengerikan. Pertimbangkan toString()danprintTo(Stream stream)
candied_orange
@ caland itu bukan (hanya) tentang nama tetapi tentang apa fungsinya. Kode itu hanya tentang detail presentasi dan kami secara efektif hanya mengabstraksi output bukan konten yang diproses dan logikanya.
Adriano Repetti
@AdrianoRepetti: jujur, ini hanya contoh yang dibuat-buat, agar sesuai dengan pertanyaan awal dan menunjukkan poin saya. Dalam sistem nyata, tentu saja akan ada konteks di sekitarnya yang akan memungkinkan untuk membuat keputusan tentang metode dan nama yang lebih baik, tentu saja.
Doc Brown
6

Jawaban terlambat tetapi saya tidak bisa menolak.

Apakah X sebagian besar kelas menjadi Y baik atau anti-pola?

Dalam kebanyakan kasus, sebagian besar aturan, diterapkan tanpa berpikir, sebagian besar akan salah besar (termasuk yang ini).

Izinkan saya menceritakan sebuah kisah tentang kelahiran suatu benda di tengah kekacauan beberapa kode prosedur yang benar, cepat dan kotor, yang terjadi, bukan karena desain, tetapi karena putus asa.

Magang saya dan saya adalah pasangan pemrograman untuk dengan cepat membuat beberapa kode membuang untuk mengikis halaman web. Kami sama sekali tidak punya alasan untuk mengharapkan kode ini akan berumur panjang, jadi kami hanya mengeluarkan sesuatu yang berhasil. Kami mengambil seluruh halaman sebagai string dan memotong barang-barang yang kami butuhkan dengan cara yang paling rapuh yang bisa Anda bayangkan. Jangan menilai. Berhasil.

Sekarang sambil melakukan ini saya membuat beberapa metode statis untuk melakukan pemotongan. Magang saya menciptakan kelas DTO yang sangat mirip dengan Anda CatData.

Ketika saya pertama kali melihat DTO itu menyadap saya. Tahun-tahun kerusakan yang telah dilakukan Jawa pada otak saya membuat saya mundur di bidang-bidang publik. Tapi kami bekerja di C #. C # tidak perlu pengambil dan setter prematur untuk mempertahankan hak Anda untuk membuat data tidak berubah, atau dienkapsulasi nanti. Tanpa mengubah antarmuka, Anda dapat menambahkannya kapan saja. Mungkin agar Anda dapat mengatur breakpoint. Semua tanpa memberi tahu klien Anda tentang hal itu. Ya C #. Boo Java.

Jadi saya memegang lidah saya. Saya menyaksikan ketika dia menggunakan metode statis saya untuk menginisialisasi hal ini sebelum menggunakannya. Kami memiliki sekitar 14 di antaranya. Itu jelek, tapi kami tidak punya alasan untuk peduli.

Kemudian kami membutuhkannya di tempat lain. Kami mendapati diri kami ingin menyalin dan menempelkan kode. 14 baris inisialisasi dilemparkan. Itu mulai terasa menyakitkan. Dia ragu-ragu dan meminta saya untuk ide.

Dengan enggan saya bertanya, "apakah Anda akan mempertimbangkan objek?"

Dia melihat kembali DTO-nya dan mengacaukan wajahnya. "Ini adalah objek".

"Maksudku, benda yang nyata"

"Hah?"

"Biarkan aku menunjukkan sesuatu padamu. Kamu memutuskan apakah itu berguna"

Saya memilih nama baru dan cepat-cepat mengambil sesuatu yang tampak seperti ini:

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);
    }
}

Ini tidak melakukan apa pun yang belum dilakukan metode statis. Tapi sekarang saya mengisap 14 metode statis ke dalam kelas di mana mereka bisa sendirian dengan data yang mereka kerjakan.

Saya tidak memaksa magang saya untuk menggunakannya. Saya hanya menawarkannya dan membiarkannya memutuskan apakah dia ingin tetap menggunakan metode statis. Saya pulang ke rumah berpikir dia mungkin akan tetap pada apa yang sudah dia kerjakan. Hari berikutnya saya menemukan dia menggunakannya di banyak tempat. Itu mendeklarasikan sisa kode yang masih jelek dan prosedural tetapi kompleksitas ini sekarang tersembunyi dari kita di belakang suatu objek. Itu sedikit lebih baik.

Sekarang yakin setiap kali Anda mengaksesnya ini melakukan sedikit pekerjaan yang adil. DTO adalah nilai cache cepat yang bagus. Saya khawatir tentang itu tetapi menyadari bahwa saya bisa menambahkan caching jika kita perlu tanpa menyentuh salah satu kode yang digunakan. Jadi saya tidak akan repot sampai kita peduli.

Apakah saya mengatakan Anda harus selalu menempel pada objek OO di atas DTO? Tidak. DTO bersinar ketika Anda harus melewati batas yang membuat Anda tidak bisa bergerak. DTO punya tempat mereka.

Tapi begitu juga objek OO. Pelajari cara menggunakan kedua alat. Pelajari berapa biaya masing-masing. Belajarlah untuk membiarkan masalah, situasi, dan magang memutuskan. Dogma bukan temanmu di sini.


Karena jawaban saya sudah lama sekali, izinkan saya membebaskan Anda dari beberapa kesalahpahaman dengan meninjau kode Anda.

Sebagai contoh, suatu kelas biasanya memiliki anggota dan metode kelas, misalnya:

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
    }
}

Di mana konstruktor Anda? Ini tidak cukup menunjukkan kepada saya untuk mengetahui apakah itu berguna.

Tetapi setelah membaca tentang prinsip tanggung jawab tunggal dan prinsip tertutup terbuka, saya lebih suka memisahkan kelas menjadi kelas DTO dan pembantu dengan metode statis saja, misalnya:

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
    }
}

Saya pikir ini sesuai dengan prinsip tanggung jawab tunggal karena sekarang tanggung jawab CatData adalah hanya menyimpan data, tidak peduli dengan metode (juga untuk CatMethods).

Anda dapat melakukan banyak hal konyol atas nama Prinsip Tanggung Jawab Tunggal. Saya bisa berpendapat bahwa Cat Strings dan Cat int harus dipisahkan. Metode menggambar dan Gambar itu semua harus memiliki kelas sendiri. Bahwa program Anda yang sedang berjalan adalah tanggung jawab tunggal sehingga Anda hanya boleh memiliki satu kelas. : P

Bagi saya, cara terbaik untuk mengikuti Prinsip Tanggung Jawab Tunggal adalah dengan menemukan abstraksi yang baik yang memungkinkan Anda memasukkan kerumitan dalam sebuah kotak sehingga Anda dapat menyembunyikannya. Jika Anda bisa memberikannya nama baik yang membuat orang tidak terkejut dengan apa yang mereka temukan ketika mereka melihat ke dalam, Anda telah mengikutinya dengan cukup baik. Mengharapkannya untuk mendikte lebih banyak keputusan maka itu meminta masalah. Jujur, kedua daftar kode Anda melakukan itu jadi saya tidak melihat mengapa SRP penting di sini.

Dan itu juga sesuai dengan prinsip tertutup terbuka karena menambahkan metode baru tidak perlu mengubah kelas CatData.

Baik tidak Prinsip buka tutup bukan tentang menambahkan metode baru. Ini tentang bisa mengubah implementasi metode lama dan tidak perlu mengedit apa pun. Tidak ada yang menggunakan Anda dan bukan metode lama Anda. Sebaliknya Anda menulis beberapa kode baru di tempat lain. Suatu bentuk polimorfisme akan melakukannya dengan baik. Jangan lihat itu di sini.

Pertanyaan saya adalah, apakah ini baik atau anti-pola?

Nah, bagaimana saya bisa tahu? Lihat, melakukannya bagaimanapun memiliki manfaat dan biaya. Ketika Anda memisahkan kode dari data, Anda dapat mengubah salah satu tanpa harus mengkompilasi ulang yang lain. Mungkin itu sangat penting bagi Anda. Mungkin itu hanya membuat kode Anda rumit tanpa tujuan.

Jika itu membuat Anda merasa lebih baik, Anda tidak jauh dari sesuatu yang Martin Fowler sebut sebagai objek parameter . Anda tidak harus hanya mengambil primitif ke objek Anda.

Apa yang saya ingin Anda lakukan adalah mengembangkan rasa untuk melakukan pemisahan Anda, atau tidak, dalam gaya pengkodean. Karena percaya atau tidak Anda tidak dipaksa untuk memilih gaya. Anda hanya harus hidup dengan pilihan Anda.

candied_orange
sumber
2

Anda telah menemukan topik perdebatan yang telah menciptakan argumen di antara pengembang selama lebih dari satu dekade. Pada tahun 2003, Martin Fowler menciptakan ungkapan "Anemic Domain Model" (ADM) untuk menggambarkan pemisahan data dan fungsionalitas ini. Dia - dan orang lain yang setuju dengannya - berpendapat bahwa "Rich Domain Models" (pencampuran data dan fungsionalitas) adalah "OO yang tepat", dan bahwa pendekatan ADM adalah anti-pola non-OO.

Selalu ada orang-orang yang menolak argumen ini dan sisi argumen tersebut telah tumbuh lebih keras dan lebih berani dalam beberapa tahun terakhir dengan adopsi oleh lebih banyak pengembang teknik pengembangan fungsional. Pendekatan itu secara aktif mendorong pemisahan data dan masalah fungsi. Data harus berubah sebanyak mungkin, sehingga enkapsulasi dari keadaan yang bisa berubah menjadi tidak menjadi perhatian. Tidak ada manfaatnya melampirkan fungsi secara langsung ke data tersebut dalam situasi seperti itu. Maka apakah "bukan OO" atau tidak sama sekali tidak menarik bagi orang-orang seperti itu.

Terlepas dari sisi mana Anda duduk di pagar (saya duduk sangat kuat di sisi "Martin Fowler berbicara banyak tosh" BTW), Anda menggunakan metode statis untuk printInfodan drawhampir universal disukai. Metode statis sulit untuk mengejek ketika menulis unit test. Jadi jika mereka memiliki efek samping (seperti mencetak atau menggambar ke beberapa layar atau perangkat lain), mereka tidak boleh statis, atau harus memiliki lokasi keluaran yang dilewatkan sebagai parameter.

Jadi Anda bisa memiliki antarmuka:

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

Dan implementasi yang disuntikkan ke seluruh sistem Anda saat runtime (dengan implementasi lain yang digunakan untuk pengujian):

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
    }
}

Atau tambahkan parameter tambahan untuk menghilangkan efek samping dari metode tersebut:

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
    }
}
David Arno
sumber
Tetapi mengapa Anda mencoba untuk menyemir bahasa OO seperti Java menjadi bahasa fungsional, alih-alih menggunakan bahasa fungsional sejak awal? Apakah ini semacam "Aku benci Jawa, tetapi semua orang menggunakannya jadi aku harus, tapi setidaknya aku akan menulisnya dengan caraku sendiri". Kecuali jika Anda mengklaim bahwa paradigma OO sudah usang dan ketinggalan zaman. Saya berlangganan sekolah pemikiran "Jika Anda menginginkan X, Anda tahu di mana mendapatkannya".
Kayaman
@ Kayaman, " Saya berlangganan" Jika Anda ingin X, Anda tahu di mana mendapatkannya "sekolah pemikiran ". Bukan saya. Saya menemukan sikap itu sebagai pandangan pendek dan sedikit ofensif. Saya tidak menggunakan Java, tetapi saya menggunakan C # dan saya mengabaikan beberapa fitur "OO nyata". Saya tidak peduli untuk warisan, objek stateful dan sejenisnya dan saya menulis banyak metode statis dan menyegel kelas (final). Tooling dan ukuran komunitas sekitar C # tidak ada duanya. Untuk F #, itu jauh lebih miskin. Jadi saya berlangganan sekolah pemikiran "Jika Anda menginginkan X, mintalah agar X ditambahkan ke perangkat Anda saat ini".
David Arno
1
Saya akan mengatakan mengabaikan aspek suatu bahasa jauh berbeda dari mencoba menggabungkan dan mencocokkan fitur dari bahasa lain. Saya tidak mencoba untuk menulis "OO nyata" baik, karena mencoba untuk hanya mengikuti aturan (atau prinsip) dalam ilmu eksak seperti pemrograman tidak berfungsi. Tentu saja Anda perlu tahu aturannya sehingga Anda bisa melanggarnya. Mengatasi fitur secara membabi buta bisa berdampak buruk bagi perkembangan bahasa. Jangan tersinggung, tetapi IMHO bagian dari adegan FP tampaknya merasa "FP adalah hal terbesar sejak roti irisan, mengapa orang tidak akan memahaminya". Saya tidak akan pernah mengatakan (atau berpikir) OO atau Java adalah "yang terbaik".
Kayaman
1
@ Kayaman, tapi apa artinya "mencoba menggabungkan dan mencocokkan fitur dari bahasa lain"? Apakah Anda berpendapat bahwa fitur fungsional tidak boleh ditambahkan ke java, karena "Java adalah bahasa OO"? Jika demikian, itu adalah argumen berpandangan pendek dalam pandangan saya. Biarkan bahasa berkembang dengan keinginan pengembang. Memaksa mereka untuk beralih ke bahasa lain adalah pendekatan yang salah, saya rasa. Tentu saja beberapa "pendukung FP" membahas tentang FP sebagai hal terbaik yang pernah ada. Dan beberapa "pendukung OO" membahas tentang OO setelah "memenangkan pertempuran karena itu jelas superior". Abaikan mereka berdua.
David Arno
1
Saya tidak anti-FP. Saya menggunakannya di Jawa di mana itu berguna. Tetapi jika seorang programmer mengatakan "Saya ingin ini" itu seperti seorang klien yang mengatakan "Saya ingin ini". Programmer bukan desainer bahasa dan klien bukan programmer. Saya mengubah jawaban Anda karena Anda mengatakan bahwa "solusi" OP disukai. Komentar dalam pertanyaan lebih ringkas, yaitu dia menciptakan kembali pemrograman prosedural dalam bahasa OO. Gagasan umum memang ada benarnya, seperti yang Anda tunjukkan. Model domain non-anemia tidak berarti data dan perilaku bersifat eksklusif, dan penyaji atau presenter luar akan dilarang.
Kayaman
0

DTO - Objek Transport Data

berguna untuk hal itu. Jika Anda akan men-shunt data antar program atau sistem dari yang diinginkan DTO karena mereka memberikan sub-set yang dikelola dari objek yang hanya berkaitan dengan struktur data dan format. Keuntungannya adalah Anda tidak harus menyinkronkan pembaruan ke metode kompleks pada beberapa sistem (selama data yang mendasarinya tidak berubah).

Inti dari OO adalah untuk mengikat data dan kode yang bekerja pada data tersebut secara berdekatan. Memisahkan Obyek yang logis ke dalam kelas yang terpisah biasanya merupakan ide yang buruk.

James Anderson
sumber
-1

Banyak pola desain dan prinsip yang bertentangan, dan pada akhirnya hanya penilaian Anda sebagai programmer yang baik yang akan membantu Anda memutuskan pola mana yang harus diterapkan untuk masalah tertentu.

Menurut pendapat saya, prinsip Do The Simplest Thing That Could Possiblely seharusnya menang secara default kecuali Anda memiliki alasan kuat untuk membuat desain lebih rumit. Jadi, dalam contoh spesifik Anda, saya akan memilih desain pertama, yang merupakan pendekatan berorientasi objek yang lebih tradisional dengan data dan fungsi bersama-sama di kelas yang sama.

Berikut adalah beberapa pertanyaan yang dapat Anda tanyakan pada diri sendiri tentang aplikasi:

  • Apakah saya perlu memberikan cara alternatif untuk merender gambar Cat? Jika demikian, bukankah warisan akan menjadi cara yang nyaman untuk melakukan itu?
  • Apakah kelas Cat saya perlu mewarisi dari kelas lain atau memenuhi antarmuka?

Menjawab ya untuk semua ini dapat mengarahkan Anda ke desain yang lebih kompleks dengan DTO dan kelas fungsional yang terpisah.

Jordan Rieger
sumber
-1

Apakah ini pola yang baik?

Anda mungkin akan mendapatkan jawaban berbeda di sini karena orang mengikuti aliran pemikiran yang berbeda. Jadi bahkan sebelum saya mulai: Jawaban ini sesuai dengan pemrograman berorientasi objek seperti yang dijelaskan oleh Robert C. Martin dalam buku "Clean Code".


"Benar" OOP

Sejauh yang saya ketahui, ada 2 interpretasi OOP. Bagi kebanyakan orang, itu bermuara pada "objek adalah kelas".

Namun, saya menemukan aliran pemikiran lain yang lebih bermanfaat: "Objek adalah sesuatu yang melakukan sesuatu". Jika Anda bekerja dengan kelas, setiap kelas akan menjadi salah satu dari dua hal: " pemegang data " atau objek . Pemegang data menyimpan data (duh), benda melakukan hal-hal. Itu tidak berarti bahwa pemegang data tidak dapat memiliki metode! Pikirkan list: A listtidak benar - benar melakukan apa - apa, tetapi ia memiliki metode, untuk menegakkan cara ia menyimpan data .

Pemegang data

Anda Catadalah contoh utama dari pemegang data. Tentu, di luar program Anda, kucing adalah sesuatu yang melakukan sesuatu , tetapi di dalam program Anda, a Catadalah String name, int, weightdan Image image.

Bahwa pemegang data tidak melakukan apa - apa juga tidak berarti bahwa mereka tidak mengandung logika bisnis! Jika Catkelas Anda membutuhkan konstruktor name, weightdan image, Anda telah berhasil merangkum aturan bisnis yang dimiliki setiap kucing. Jika Anda bisa mengubah weightsetelah Catkelas dibuat, itu aturan bisnis lain yang dienkapsulasi. Ini adalah hal-hal yang sangat mendasar, tetapi itu artinya sangat penting untuk tidak membuat kesalahan - dan dengan cara ini, Anda memastikan bahwa tidak mungkin melakukan kesalahan itu.

Benda

Objek melakukan sesuatu. Jika Anda ingin objek Clean *, kita dapat mengubahnya menjadi "Objects do one thing".

Mencetak info kucing adalah pekerjaan suatu objek. Misalnya, Anda dapat memanggil objek ini " CatInfoLogger", dengan metode publik Log(Cat). Hanya itu yang perlu kita ketahui dari luar: Objek ini mencatat info kucing dan untuk melakukannya, diperlukan a Cat.

Di bagian dalam, objek ini akan memiliki referensi ke apa pun yang dibutuhkan untuk memenuhi tanggung jawab tunggal dari penebangan kucing. Dalam hal ini, itu hanya referensi Systemuntuk System.out.println, tetapi dalam kebanyakan kasus, itu akan menjadi referensi pribadi ke objek lain. Jika logika untuk memformat output sebelum mencetaknya menjadi terlalu kompleks, CatInfoLoggerbisa saja mendapatkan referensi ke objek baru CatInfoFormatter. Jika nanti, setiap log juga harus ditulis ke file, Anda dapat menambahkan CatToFileLoggerobjek yang melakukan itu.

Pemegang data vs objek - ringkasan

Sekarang, mengapa Anda melakukan semua itu? Karena sekarang mengubah kelas hanya mengubah satu hal ( cara kucing masuk dalam contoh). Jika Anda mengubah suatu objek, Anda hanya mengubah cara tindakan tertentu dilakukan; jika Anda mengubah pemegang data, Anda hanya mengubah data apa yang disimpan dan / atau dengan cara apa data tersebut disimpan.

Mengubah data mungkin berarti cara sesuatu dilakukan harus berubah juga, tetapi perubahan itu tidak akan terjadi sampai Anda membuatnya sendiri dengan mengubah objek yang bertanggung jawab.

Berlebihan?

Solusi ini mungkin tampak sedikit berlebihan. Biarkan aku jujur: Ini adalah . Jika keseluruhan program Anda hanya sebesar contoh, jangan lakukan salah satu di atas - cukup pilih salah satu cara yang Anda usulkan dengan lemparan koin. Tidak ada bahaya membingungkan kode lain jika ada yang tidak ada kode lain.

Namun, perangkat lunak "serius" (= lebih dari satu skrip atau 2) mungkin cukup besar sehingga akan mendapat untung dari struktur seperti ini.


Menjawab

Apakah memisahkan sebagian besar kelas menjadi DTO dan kelas pembantu [...] baik atau anti-pola?

Mengubah "sebagian besar kelas" (tanpa kualifikasi lebih lanjut) menjadi DTO / pemegang data, bukan merupakan pola anti, karena sebenarnya bukan pola apa pun - lebih atau kurang acak.

Namun, memisahkan data dan objek dianggap sebagai cara yang baik untuk menjaga kode Anda Bersih *. Kadang-kadang Anda hanya perlu DTO datar, "anemia" dan hanya itu. Di lain waktu, Anda memerlukan metode untuk menegakkan cara data disimpan. Hanya saja, jangan menaruh fungsionalitas program Anda dengan data.


* Itu "Bersih" dengan huruf C seperti judul buku, karena - ingat paragraf pertama - ini adalah tentang satu aliran pemikiran, sementara ada juga yang lain.

R. Schmitz
sumber