Pola desain untuk nilai-nilai yang saling bergantung

8

Ringkasan: Apakah ada pola desain yang baik untuk mengurangi duplikasi informasi di antara nilai-nilai yang saling bergantung erat?

Dalam pekerjaan saya, cukup umum untuk memiliki hubungan antara jumlah sehingga Anda dapat memperoleh salah satu dari jumlah tersebut jika Anda tahu yang lain. Contohnya adalah hukum gas Ideal :

Pv = RT

Anda bisa membayangkan membuat kelas untuk mewakili keadaan gas ideal. Kelas akan memiliki 3 sifat, secara alami Pressure, Temperaturedan SpecificVolumemasing-masing jenis yang tepat.

Untuk pengguna objek kelas ini, akan wajar untuk mengharapkan bahwa jika Anda menetapkan nilai untuk keduanya Pressuredan Temperature, Anda kemudian bisa membaca nilai untuk SpecificVolumedan berharap objek telah menghitungnya untuk Anda.

Demikian juga, jika Anda menetapkan nilai untuk keduanya Pressuredan SpecificVolume, Anda bisa membacanya Temperature, dll.

Namun untuk benar-benar mengimplementasikan kelas ini diperlukan duplikasi informasi. Anda perlu memprogram semua variasi persamaan secara eksplisit, memperlakukan variabel yang berbeda sebagai dependen dalam setiap kasus:

  1. T = P * v / R

  2. P = R * T / v

  3. v = R * T / P

yang tampaknya melanggar prinsip KERING . Meskipun masing-masing menyatakan hubungan yang sama, kasus-kasus ini membutuhkan pengkodean & pengujian independen.

Dalam kasus nyata, logika yang saya pikirkan lebih kompleks dari contoh ini, tetapi menunjukkan masalah dasar yang sama. Jadi akan ada nilai nyata jika saya bisa mengekspresikan logika hanya sekali, atau setidaknya lebih sedikit.

Perhatikan bahwa kelas seperti ini mungkin juga harus berurusan dengan memastikan bahwa itu diinisialisasi dengan benar sebelum nilai akan dibacakan, tapi saya pikir itu adalah pertimbangan sekunder.


Meskipun saya memberikan contoh matematika pertanyaannya tidak terbatas hanya pada hubungan matematika antara data. Itu hanya tampak sebagai contoh sederhana untuk menjelaskan maksudnya.

UuDdLrLrSs
sumber
5
Ingatlah bahwa contoh Anda tidak menunjukkan duplikasi dalam bentuk apa pun. KERING hanya berlaku ketika Anda secara aktif membuat kembali perilaku yang sama . Dalam contoh kasus Anda, setiap variasi persamaan memecahkan nilai yang berbeda - dan karenanya, merupakan perilaku yang berbeda. Bentuk Pv = RT dari persamaan adalah generalisasi yang tidak berguna dengan sendirinya dari sudut pandang OO.
T. Sar
Saya tidak melihat duplikasi.
Berhentilah melukai Monica
1
Ada juga masalah membuat negara "ilegal" tidak mungkin, yaitu negara dengan Pv/T != R. Tidak yakin apakah gagasan itu dapat membantu Anda memecahkan masalah yang mendasarinya, atau apakah itu menyulitkannya.
Bernhard Hiller
Seperti yang dikatakan T. Sar, tidak ada pengulangan di sini. Hal lain yang disarankan Martin Maat pasti akan memicu lebih banyak masalah yang akan diselesaikan. Pada dasarnya pencarian Anda untuk mesin yang dapat secara otomatis mengadaptasi formula untuk menghitung nilai yang hilang dari yang lain, kecuali jika Anda menemukan perpustakaan yang melakukan itu (siapa yang tahu ...) menyerah begitu saja, itu tidak sebanding dengan masalahnya.
Walfrat
Seperti jawaban yang ditunjukkan, bahasa Imperatif memiliki beberapa masalah. Dengan Kontras, Bahasa Deklaratif / Logika seperti Prolog membuatnya sepele. Periksa Wikipedia . Ini dapat dilakukan dengan bahasa Imperatif, "hanya" membutuhkan lebih banyak pekerjaan.
Armaghast

Jawaban:

12

Tidak ada masalah dari perspektif OO. Anda bisa memiliki metode penawaran GasLaw kelas statis

GetVolume(R, T, P) 
GetPressure(R, T, v)

dan lain-lain

dan tidak akan ada masalah duplikasi. Tidak ada objek, tidak ada anggota data. Hanya perilaku yang semuanya berbeda.

Anda dapat menganggap hukum gas sebagai "satu hal" tetapi operasinya berbeda. Tidak ada yang salah dengan memiliki metode untuk setiap kasus penggunaan hukum.

Martin Maat
sumber
3
Ini tidak benar-benar menjawab pertanyaan tentang bagaimana secara internal di kelas seperti itu untuk mengurangi duplikasi informasi / logika. Antarmuka eksternal sebenarnya tidak masalah.
UuDdLrLrSs
Saya tidak setuju bahwa itu tidak menjawab pertanyaan. Jawabannya jelas bagi saya bahwa Martin mengatakan jangan lakukan apa yang Anda minta bagaimana melakukannya karena jelas jauh lebih mudah untuk memahami cara mencapai hal yang sama persis.
Dunk
10

Ini tidak mungkin terjadi dengan cara Anda bertanya.

Pertimbangkan: Saya memiliki objek gas yang ideal g. Jika saya secara eksplisit mengatur ketiganya dari suhu, tekanan dan volume tertentu, dan kemudian mendapatkan suhu lagi, seperti:

IdealGas g;
g.setTemperature(t1);
g.setPressure(p1);
g.setVolume(v1);
assert(g.getTemperature() == what); // ?

haruskah itu:

  1. beri saya nilai yang t1awalnya saya tetapkan?
  2. menghitung nilai menggunakan tekanan dan volume spesifik?
  3. menghitung nilai menggunakan tekanan dan volume spesifik hanya ketika saya mengaturnya setelah suhu?
  4. beri saya nilai asli jika cukup dekat dengan yang dihitung (kita harus mengizinkan kesalahan pembulatan), dan menghitung sebagai # 2 atau # 3 sebaliknya?

Jika Anda harus melakukan ini, Anda perlu melacak banyak status untuk setiap anggota: apakah tidak diinisialisasi, ditetapkan secara eksplisit oleh kode klien, atau caching nilai yang dihitung? Apakah nilai yang ditetapkan secara eksplisit atau di-cache dibatalkan oleh anggota lain di kemudian hari?

Perilaku ini jelas sulit diprediksi dari membaca kode klien. Ini adalah desain yang buruk, karena akan selalu sulit untuk mencari tahu mengapa Anda mendapatkan jawaban yang Anda lakukan. Kompleksitas adalah tanda bahwa masalah ini sangat tidak cocok untuk OO, atau setidaknya bahwa objek gas ideal stateful adalah pilihan abstraksi yang buruk.

Tak berguna
sumber
8

Pertama saya tidak berpikir Anda melanggar Prinsip KERING karena ketiga rumus menghitung nilai yang berbeda. Faktanya itu adalah ketergantungan antara ketiga nilai tersebut karena Anda melihatnya sebagai persamaan matematika dan bukan sebagai tugas variabel terprogram.

Saya sarankan untuk menerapkan kasus Anda dengan kelas IdealGas yang tidak berubah sebagai berikut

public class IdealGas {
    private static final double R = 8.3145;

    private final Pressure            pressure;
    private final Temperature         temperature;
    private final SpecificVolume      specificVolume;

    public IdealGas(Pressure pressure, Temperature temperature)
    {
        this.pressure = pressure;
        this.temperature = temperature;
        this.specificVolume = calculateSpecificVolume(pressure.getValue(), temperature.getValue());
    }


    public IdealGas(Pressure pressure, SpecificVolume specificVolume)
    {
        this.pressure = pressure;
        this.specificVolume = specificVolume;
        this.temperature = calculateTemperature(pressure.getValue(), specificVolume.getValue());
    }

    public IdealGas(Temperature temperature, SpecificVolume specificVolume)
    {
        this.temperature = temperature;
        this.specificVolume = specificVolume;
        this.pressure = calculatePressure(temperature.getValue(), specificVolume.getValue());
    }

    private SpecificVolume calculateSpecificVolume(double pressure, double temperature)
    {
        return new SpecificVolume(R * temperature / pressure);
    }

    private Temperature calculateTemperature(double pressure, double specificVolume)
    {
        return new Temperature(pressure * specificVolume / R);
    }

    private Pressure calculatePressure(double temperature, double specificVolume)
    {
        return new Pressure(R * temperature / specificVolume);
    }

    public Pressure getPressure()
    {
        return pressure;
    }

    public Temperature getTemperature()
    {
        return temperature;
    }

    public SpecificVolume getSpecificVolume()
    {
        return specificVolume;
    }
}

Biarkan saya jelaskan implementasinya. Kelas IdealGas merangkum 3 properti Pressure, Temperature dan SpecificVolume sebagai nilai akhir untuk kekekalan. Setiap kali Anda menelepon getXXX, perhitungan tidak akan dilakukan. Caranya adalah memiliki 3 konstruktor untuk semua 3 kombinasi dari 2 parameter yang diberikan. Di setiap konstruktor Anda menghitung variabel ketiga yang hilang. Perhitungan dilakukan satu kali, pada waktu konstruksi dan ditetapkan ke atribut ketiga. Karena ini dilakukan dalam konstruktor, atribut ketiga dapat bersifat final dan tidak berubah. Untuk kekekalan saya berasumsi bahwa kelas Tekanan, Suhu dan Spesifik Volume juga tidak berubah.

Satu-satunya bagian yang tersisa adalah memiliki getter untuk semua atribut. Tidak ada setter karena Anda meneruskan parameter ke konstruktor. Jika Anda perlu mengubah atribut, buat instance IdealGas baru dengan parameter yang diinginkan.

Dalam contoh saya, kelas Pressure, Temperature, dan SpecificVolume adalah pembungkus sederhana dengan nilai ganda. Kode sampel dalam java, tetapi dapat digeneralisasi.

Pendekatan ini dapat digeneralisasi, meneruskan semua data terkait ke dalam konstruktor, menghitung data terkait dalam konstruktor dan hanya memiliki getter.

catta
sumber
2
Memecahkan ini dengan immutability dan injeksi konstruktor adalah jawaban yang tepat di sini. Dalam bahasa OO, tiga variasi persamaan yang berbeda diterjemahkan menjadi tiga konstruktor yang berbeda. Setiap operasi pada IdealGas menghasilkan objek lain (nilai). +1 Ini harus menjadi jawabannya di sini.
Greg Burghardt
+1 untuk memberikan solusi
keuleJ
5

Ini pertanyaan yang sangat menarik! Anda pada prinsipnya benar bahwa Anda menggandakan informasi karena persamaan yang sama digunakan dalam ketiga kasus, hanya dengan perbedaan yang tidak diketahui.

Tetapi dalam bahasa pemrograman arus utama yang khas tidak ada dukungan bawaan untuk memecahkan persamaan. Variabel dalam pemrograman selalu diketahui jumlahnya (pada saat eksekusi), jadi meskipun ada persamaan yang dangkal, ekspresi dalam bahasa pemrograman tidak dapat dibandingkan dengan persamaan matematika.

Dalam aplikasi tipikal, persamaan seperti yang Anda gambarkan hanya akan ditulis sebagai tiga ekspresi terpisah dan Anda hidup dengan duplikasi. Tapi perpustakaan pemecahan persamaan memang ada dan bisa digunakan dalam kasus seperti itu.

Ini lebih dari sebuah pola, itu adalah paradigma pemrograman keseluruhan, yang disebut penyelesaian kendala, dan ada bahasa pemrograman khusus seperti Prolog untuk masalah seperti ini.

JacquesB
sumber
3

Ini adalah iritasi umum ketika meng-encode model matematika ke dalam perangkat lunak. Keduanya menggunakan notasi yang sangat mirip untuk model sederhana seperti hukum gas, tetapi bahasa pemrograman bukan matematika dan banyak hal yang dapat Anda tinggalkan saat bekerja di dunia matematika harus dilakukan secara eksplisit dalam perangkat lunak.

Itu tidak mengulangi dirimu sendiri. Tidak ada konsep persamaan, transformasi aljabar atau "penyelesaian untuk x" di sebagian besar bahasa pemrograman. Tanpa representasi perangkat lunak dari semua konsep tersebut, tidak ada cara bagi perangkat lunak untuk sampai pada persamaan T = P * v / Ryang diberikan Pv = RT.

Sementara itu mungkin tampak seperti batasan yang tidak masuk akal dari bahasa pemrograman ketika melihat model-model sederhana seperti hukum gas, bahkan matematika yang sedikit lebih maju memungkinkan persamaan yang tidak memiliki solusi aljabar tertutup, atau tidak ada metode yang dikenal yang dapat secara efisien memperoleh solusi. Untuk arsitektur perangkat lunak Anda tidak dapat bergantung padanya dalam kasus yang lebih kompleks.

Dan kasus sederhana dengan sendirinya? Saya tidak yakin apakah itu layak untuk membaca spesifikasi fitur dalam bahasa pemrograman. Model cenderung diganti, tidak dimodifikasi dan biasanya hanya memiliki beberapa variabel untuk dipecahkan.

Patrick
sumber
1

Kelas akan memiliki 3 sifat, secara alami Pressure, Temperaturedan SpecificVolume.

Tidak, dua saja sudah cukup. Tapi membuat mereka pribadi dan mengekspos metode sebagai pressure(), temperature(), dan specificVolume(). Salah satunya, tidak sesuai dengan dua properti pribadi, harus memiliki logika yang sesuai. Dengan demikian kita bisa menghilangkan duplikasi data.

Seharusnya ada tiga konstruktor dengan parameter (P, T), (P, v)dan (T, v). Salah satunya, sesuai dengan dua properti pribadi, hanya bertindak sebagai pemukim. Dua lainnya harus memiliki logika yang sesuai. Tentu saja logika ditulis tiga kali (dua kali di sini dan satu kali dalam paragraf sebelumnya), tetapi mereka bukan duplikat. Bahkan jika Anda menganggapnya sebagai duplikat, mereka dibutuhkan.

Apakah ketiga ungkapan ini mengekspresikan hubungan yang sama?

Ya secara matematis dan tidak ada OO-ly. Dalam objek OO adalah warga negara kelas satu tidak ekspresi. Untuk membuat ekspresi warga negara kelas pertama (atau untuk membuat hubungan diungkapkan oleh hal yang sama) kita perlu menulis kelas, katakanlah, Expressionatau Equationyang bukan tugas yang mudah (mungkin ada beberapa perpustakaan).

Pertanyaannya tidak terbatas hanya pada hubungan matematika antara data.

Hubungan juga bukan warga negara kelas satu. Untuk itu kita mungkin perlu menulis kelas Relationshipyang juga tidak mudah. Tetapi hidup dengan duplikat lebih mudah.

Jika Anda memutuskan untuk hidup dengan duplikat dan jika parameter dalam hubungan tinggi pertimbangkan menggunakan pola Builder . Sekalipun parameternya kurang pertimbangkan menggunakan Pabrik Statis untuk menghindari batasan overloading konstruktor.

Durgadass S
sumber
1

Anda bisa membayangkan membuat kelas untuk mewakili keadaan gas ideal.

Kelas harus mewakili perilaku gas ideal. Sebuah instance kelas - sebuah objek - mewakili negara.

Ini bukan memilih nits. Desain kelas harus dari perspektif perilaku dan fungsionalitas. Perilaku tersebut diekspos secara publik sehingga objek yang dipakai (ya, itu berlebihan) mencapai keadaan melalui perilaku berolahraga.

Begitu:

Gas.HeatToFarenheit( 451 );

Dan berikut ini hanya omong kosong dan tidak mungkin di dunia nyata. Dan jangan lakukan itu dalam kode juga:

Gas.MoleculeDistiance = 2.34;  //nanometers

Perilaku membuat moot pertanyaan "duplikat informasi"

Mempengaruhi properti negara yang sama melalui metode yang berbeda bukanlah duplikasi. Berikut ini juga mempengaruhi suhu, tetapi ini bukan duplikat dari yang di atas:

Gas.PressurizeTo( 34 );  //pascals
radarbob
sumber
0

Mungkin Anda bisa menggunakan pembuatan kode untuk menghasilkan berbagai bentuk persamaan dari bentuk tunggal yang Anda tentukan.

Saya dapat membayangkannya akan cukup sulit untuk formula yang lebih kompleks. Tetapi untuk yang sederhana, Anda hanya perlu memberi

  • memindahkan variabel dari satu sisi ke sisi lain berdasarkan pembagian
  • balik sisi kiri dan kanan pada persamaan

Saya bisa membayangkan jika Anda menambahkan perkalian, penambahan dan pengurangan ke daftar itu dan rumus dasar Anda memiliki masing-masing variabel hanya sekali maka Anda dapat menggunakan manipulasi string untuk secara otomatis menghasilkan semua versi formula dengan kode boilerplate yang tepat untuk menggunakan yang benar mengingat variabel yang diketahui dikenal .

Wolfram Alpha memiliki berbagai alat manipulasi persamaan misalnya.

Namun!! kecuali Anda memiliki daftar besar kelas-kelas ini untuk menghasilkan saya tidak dapat melihat kode seperti itu sebagai penggunaan waktu Anda secara efektif.

Anda hanya perlu membuat kode ini sekali per fungsi dan fungsi Anda cenderung terlalu rumit untuk dipecahkan sesederhana contoh Anda.

Manual coding setiap versi kemungkinan menjadi metode tercepat dan paling dapat diandalkan menghasilkan fungsi dan meskipun Anda bisa membayangkan solusi di mana Anda kode untuk iteratif menebak nilai dan tes untuk kebenaran, saya pikir Anda lakukan kebutuhan untuk menghasilkan dan mengkompilasi fungsi untuk menghitung variabel yang tidak diketahui dengan jumlah daya pemrosesan yang masuk akal.

Ewan
sumber
0

Saya pikir Anda terlalu memikirkan ini, pertimbangkan solusi berikut:

double computeIdealGasLaw(double a, double b, double c){
    return a * b / c;
}
// example
//T = P * v/R
double P = ....;
double v = ....;
// R is a constant;
double T = computeIdealGasLaw(P, v, R); 


//P = R * T/v
double T = ....;
double v = ....;
// R is a constant;
double P = computeIdealGasLaw(R, T, v); 

//v = R * T/P
double T = ....;
double P = ....;
// R is a constant;
double v = computeIdealGasLaw(R, T, P); 

Anda hanya perlu mendefinisikan satu fungsi, tidak ada duplikasi, Anda bahkan bisa menggunakan ini untuk implementasi kelas internal, atau Anda bisa menggunakan fungsi ini Anda cukup mengubah parameter mana yang Anda gunakan di mana.

Anda juga dapat menggunakan pola yang sama ini untuk situasi apa pun di mana Anda memiliki fungsi di mana nilai ditukar.

Jika Anda menggunakan langauge yang diketik secara statis dan harus berurusan dengan fungsi di mana nilainya bisa menjadi tipe yang berbeda, Anda bisa menggunakan pemrograman template (catatan, menggunakan sintaks C ++)

template<class A, class B, class C, class D>
D computeIdealGasLaw(A a, B b, C c){
    return D(a * b / c);
}
wih
sumber