Saya tidak yakin pola desain mana yang dapat membantu saya mengatasi masalah ini.
Saya memiliki kelas, 'Koordinator', yang menentukan kelas Pekerja mana yang harus digunakan - tanpa harus tahu tentang semua jenis Pekerja yang ada - hanya memanggil WorkerFactory dan bertindak berdasarkan antarmuka IWorker yang umum.
Kemudian menetapkan Pekerja yang sesuai untuk bekerja dan mengembalikan hasil metode 'DoWork'.
Ini baik-baik saja ... sampai sekarang; kami memiliki persyaratan baru untuk kelas Pekerja baru, "WorkerB" yang membutuhkan sejumlah informasi tambahan yaitu parameter input tambahan, agar dapat melakukan tugasnya.
Seperti kita membutuhkan metode DoWork yang kelebihan beban dengan parameter input ekstra ... tapi kemudian semua Pekerja yang ada harus menerapkan metode itu - yang tampaknya salah karena Pekerja itu benar-benar tidak memerlukan metode itu.
Bagaimana saya bisa menolak ini agar Koordinator tidak mengetahui Pekerja mana yang sedang digunakan dan masih memungkinkan setiap Pekerja untuk mendapatkan informasi yang diperlukan untuk melakukan tugasnya tetapi tidak ada Pekerja yang melakukan hal-hal yang tidak perlu?
Sudah banyak Pekerja yang ada.
Saya tidak ingin harus mengubah salah satu Pekerja beton yang ada untuk mengakomodasi persyaratan kelas WorkerB baru.
Saya pikir mungkin pola Penghias akan baik di sini, tetapi saya belum melihat Penghias menghiasi objek dengan metode yang sama tetapi parameter yang berbeda sebelum ...
Situasi dalam kode:
public class Coordinator
{
public string GetWorkerResult(string workerName, int a, List<int> b, string c)
{
var workerFactor = new WorkerFactory();
var worker = workerFactor.GetWorker(workerName);
if(worker!=null)
return worker.DoWork(a, b);
else
return string.Empty;
}
}
public class WorkerFactory
{
public IWorker GetWorker(string workerName)
{
switch (workerName)
{
case "WorkerA":
return new ConcreteWorkerA();
case "WorkerB":
return new ConcreteWorkerB();
default:
return null;
}
}
}
public interface IWorker
{
string DoWork(int a, List<int> b);
}
public class ConcreteWorkerA : IWorker
{
public string DoWork(int a, List<int> b)
{
// does the required work
return "some A worker result";
}
}
public class ConcreteWorkerB : IWorker
{
public string DoWork(int a, List<int> b, string c)
{
// does some different work based on the value of 'c'
return "some B worker result";
}
public string DoWork(int a, List<int> b)
{
// this method isn't really relevant to WorkerB as it is missing variable 'c'
return "some B worker result";
}
}
sumber
IWorker
antarmuka terdaftar versi lama, atau apakah itu versi baru dengan parameter yang ditambahkan?Coordinator
sudah harus diubah untuk mengakomodasi parameter tambahan dalamGetWorkerResult
fungsinya - itu berarti bahwa Prinsip Terbuka-Tertutup dari SOLID dilanggar. Sebagai akibatnya, semua panggilan kodeCoordinator.GetWorkerResult
harus diubah juga. Jadi lihat di tempat Anda memanggil fungsi itu: bagaimana Anda memutuskan IWorker untuk meminta? Itu mungkin mengarah pada solusi yang lebih baik.Jawaban:
Anda perlu menggeneralisasi argumen agar sesuai dengan parameter tunggal dengan antarmuka dasar dan sejumlah variabel bidang atau properti. Semacam seperti ini:
Perhatikan cek nol ... karena sistem Anda fleksibel dan terikat, ini juga bukan tipe aman, jadi Anda perlu memeriksa gips untuk memastikan argumen yang disahkan valid.
Jika Anda benar-benar tidak ingin membuat objek konkret untuk setiap kemungkinan kombinasi argumen, Anda bisa menggunakan tuple sebagai gantinya (tidak akan menjadi pilihan pertama saya.)
sumber
if (args == null) throw new ArgumentException();
Sekarang setiap konsumen IWorker harus tahu jenis konkretnya - dan antarmuka tidak berguna: Anda juga dapat membuangnya dan menggunakan jenis beton sebagai gantinya. Dan itu ide yang buruk, bukan?WorkerFactory.GetWorker
hanya dapat memiliki satu jenis kembali). Sementara di luar cakupan contoh ini, kita tahu penelepon dapat membuat sebuahworkerName
; mungkin itu bisa muncul dengan argumen yang tepat juga.Saya telah merekayasa ulang solusi berdasarkan komentar @ Dunk:
Jadi saya telah menggeser semua argumen yang mungkin diperlukan untuk membuat IWorker menjadi metode IWorerFactory.GetWorker dan kemudian masing-masing pekerja sudah memiliki apa yang dibutuhkannya dan Koordinator dapat memanggil pekerja saja. DoWork ();
sumber
Saya akan menyarankan satu dari beberapa hal.
Jika Anda ingin mempertahankan enkapsulasi, sehingga panggilan tidak perlu tahu apa-apa tentang cara kerja pekerja atau pabrik pekerja, maka Anda harus mengubah antarmuka untuk memiliki parameter tambahan. Parameter dapat memiliki nilai default, sehingga beberapa callsite masih bisa menggunakan 2 parameter. Ini akan mengharuskan perpustakaan yang mengkonsumsi dikompilasi ulang.
Pilihan lain yang saya sarankan, karena itu merusak enkapsulasi dan umumnya OOP buruk. Ini juga mengharuskan Anda setidaknya dapat memodifikasi semua panggilan untuk
ConcreteWorkerB
. Anda bisa membuat kelas yang mengimplementasikanIWorker
antarmuka, tetapi juga memilikiDoWork
metode dengan parameter tambahan. Kemudian dalam panggilan Anda, coba untuk membuangIWorker
denganvar workerB = myIWorker as ConcreteWorkerB;
dan kemudian gunakan tiga parameterDoWork
pada jenis beton. Sekali lagi, ini adalah ide yang buruk, tetapi itu adalah sesuatu yang bisa Anda lakukan.sumber
@Jtech, sudahkah Anda mempertimbangkan penggunaan
params
argumen? Ini memungkinkan sejumlah variabel parameter dilewatkan.https://msdn.microsoft.com/en-us/library/w5zay9db(v=vs.71).aspx
sumber