Kelas dengan anggota yang bisa berubah selama pembuatan tetapi tidak bisa diubah sesudahnya


22

Saya memiliki algoritma yang membuat kumpulan objek. Objek-objek ini bisa berubah selama pembuatan, karena mereka mulai dengan sangat sedikit, tetapi kemudian mereka diisi dengan data di berbagai tempat dalam algoritma.

Setelah algoritma selesai, objek tidak boleh diubah - namun dikonsumsi oleh bagian lain dari perangkat lunak.

Dalam skenario ini, apakah dianggap praktik yang baik untuk memiliki dua versi kelas, seperti yang dijelaskan di bawah ini?

  • Yang bisa berubah dibuat oleh algoritma, lalu
  • Setelah menyelesaikan algoritma, data disalin ke objek yang tidak dapat diubah yang dikembalikan.

3
Bisakah Anda mengedit pertanyaan Anda untuk mengklarifikasi masalah / pertanyaan Anda?
Simon Bergot

Anda juga mungkin tertarik dengan pertanyaan ini: Cara membekukan es loli di .NET (buat kelas tidak berubah)
R0MANARMY

Jawaban:


46

Anda mungkin dapat menggunakan pola builder . Ia menggunakan objek 'pembangun' yang terpisah dengan tujuan mengumpulkan data yang diperlukan, dan ketika semua data dikumpulkan, ia menciptakan objek yang sebenarnya. Objek yang dibuat bisa berubah.


Solusi Anda adalah yang benar untuk persyaratan saya (tidak semuanya dinyatakan). Setelah diselidiki objek yang dikembalikan tidak semua memiliki karakteristik yang sama. Kelas pembangun memiliki banyak bidang nullable dan ketika data telah dibangun, itu memilih salah satu dari 3 kelas yang berbeda untuk menghasilkan: ini terkait satu sama lain oleh warisan.
Paul Richards

2
@ Paul Dalam hal ini, jika jawaban ini menyelesaikan masalah Anda, Anda harus menandainya sebagai diterima.
Bersepeda

24

Cara sederhana untuk mencapai ini adalah dengan memiliki antarmuka yang memungkinkan seseorang untuk membaca properti dan memanggil hanya metode baca saja dan kelas yang mengimplementasikan antarmuka yang juga memungkinkan Anda menulis kelas itu.

Metode Anda yang membuatnya, berurusan dengan yang pertama, dan kemudian mengembalikan yang terakhir hanya menyediakan antarmuka baca saja untuk berinteraksi. Ini tidak memerlukan penyalinan dan memungkinkan Anda untuk dengan mudah menyempurnakan perilaku yang Anda inginkan tersedia bagi penelepon sebagai lawan pencipta.

Ambil contoh ini:

public interface IPerson 
{
    public String FirstName 
    {
        get;
    }

    public String LastName 
    {
        get;
    }
} 

public class PersonImpl : IPerson 
{
    private String firstName, lastName;

    public String FirstName 
    {
        get { return firstName; }
        set { firstName = value; }
    }

    public String LastName 
    {
        get { return lastName; }
        set { lastName = value; }
    }
}

class Factory 
{
    public IPerson MakePerson() 
    {
        PersonImpl person = new PersonImpl();
        person.FirstName = 'Joe';
        person.LastName = 'Schmoe';
        return person;
    }
}

Satu-satunya kelemahan dari pendekatan ini adalah seseorang dapat dengan mudah melemparkannya ke kelas pelaksana. Jika itu masalah keamanan, maka hanya menggunakan pendekatan ini tidak cukup. Solusi untuk ini adalah bahwa Anda dapat membuat kelas fasad untuk membungkus kelas yang bisa berubah, yang hanya menyajikan antarmuka yang bekerja dengan penelepon dan tidak dapat memiliki akses ke objek internal.

Dengan cara ini, bahkan casting tidak akan membantu Anda. Keduanya dapat berasal dari antarmuka hanya baca yang sama, tetapi casting objek yang dikembalikan hanya akan memberi Anda kelas Fasad, yang tidak berubah karena tidak mengubah keadaan mendasar dari kelas yang dapat diubah yang dibungkus.

Perlu disebutkan bahwa ini tidak mengikuti tren tipikal di mana objek abadi dibangun sekali dan untuk semua melalui konstruktornya. Dapat dimengerti, Anda mungkin harus berurusan dengan banyak parameter, tetapi Anda harus bertanya pada diri sendiri apakah semua parameter ini perlu didefinisikan di muka atau jika beberapa dapat diperkenalkan nanti. Dalam hal itu, konstruktor sederhana dengan hanya parameter yang diperlukan harus digunakan. Dengan kata lain, jangan gunakan pola ini jika menutupi masalah lain di program Anda.


1
Keamanan tidak lebih baik dengan mengembalikan objek "baca saja", karena kode yang mendapatkan objek, masih dapat memodifikasi objek menggunakan refleksi. Bahkan string dapat dimodifikasi (Tidak disalin, dimodifikasi di tempat) menggunakan refleksi.
MTilsted

Masalah keamanan dapat diselesaikan dengan rapi dengan kelas privat
Esben Skov Pedersen

@Esben: Anda masih harus bersaing dengan MS07-052: Eksekusi kode menghasilkan eksekusi kode . Kode Anda berjalan dalam konteks keamanan yang sama dengan kode mereka, sehingga mereka bisa melampirkan debugger dan melakukan apa pun yang mereka inginkan.
Kevin

Kevin1 Anda bisa mengatakan itu tentang semua enkapsulasi. Saya tidak berusaha melindungi dari refleksi.
Esben Skov Pedersen

1
Masalah dengan menggunakan kata "keamanan" adalah bahwa segera seseorang berasumsi bahwa apa yang saya katakan sebagai opsi yang lebih aman setara dengan keamanan maksimum dan praktik terbaik. Saya juga tidak pernah mengatakan. Jika Anda menyerahkan perpustakaan untuk digunakan seseorang, kecuali dikaburkan (dan terkadang bahkan dikaburkan), Anda bisa melupakan jaminan keamanan. Namun, saya pikir kita semua bisa sepakat pada fakta bahwa jika Anda merusak objek fasad yang dikembalikan menggunakan refleksi untuk mengambil objek internal yang terkandung di dalamnya Anda tidak benar-benar menggunakan perpustakaan seperti yang seharusnya digunakan.
Neil

8

Anda dapat menggunakan pola Builder seperti yang dikatakan @JacquesB, atau kalau tidak berpikir mengapa benda-benda Anda ini harus bisa berubah selama pembuatan?

Dengan kata lain, mengapa proses pembuatannya harus disebarkan dalam waktu, sebagai lawan dari menyerahkan semua nilai yang diperlukan ke dalam konstruktor dan membuat contoh dalam sekali jalan?

Karena Builder bisa menjadi solusi yang baik untuk masalah yang salah.

Jika masalahnya adalah Anda berakhir dengan konstruktor yang panjangnya 10 parameter, dan Anda ingin menguranginya dengan membangun objek sedikit demi sedikit, ini dapat mengindikasikan bahwa desainnya kacau, dan 10 nilai ini seharusnya " dikantongi "/ dikelompokkan menjadi beberapa objek ... atau objek utama terpecah menjadi beberapa yang lebih kecil ...

Dalam hal ini - berpegang teguh pada keabadian sepanjang jalan, hanya meningkatkan desain.


Akan sulit untuk membuatnya sekaligus karena kode melakukan beberapa permintaan basis data untuk mendapatkan data; ini membandingkan data dalam tabel yang berbeda, dan dalam database yang berbeda.
Paul Richards
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.