Pertama-tama, selamat untuk mengambil pemrograman selangkah lebih maju dan bertanya-tanya tentang bagaimana melakukannya dengan lebih baik (dan untuk mengajukan pertanyaan yang bagus). Ini adalah sikap yang baik dan mutlak diperlukan untuk membawa program Anda selangkah lebih maju. Pujian!
Apa yang Anda hadapi di sini adalah masalah yang terkait dengan arsitektur program Anda (atau desain, tergantung pada siapa yang Anda tanya). Ini tidak begitu banyak tentang apa yang dilakukannya, tetapi bagaimana melakukannya (yaitu struktur program Anda alih-alih fungsinya). Sangat penting untuk memperjelas hal ini: Anda benar - benar dapat membuat kelas-kelas itu mengambil File
objek sebagai input, dan program Anda masih bisa bekerja. Jika Anda melangkah lebih jauh dan menambahkan semua kode penanganan pengecualian dan menangani kasus tepi yang terkait dengan file dan I / O (yang seharusnyadilakukan di suatu tempat) di kelas-kelas itu (... tetapi tidak di sana), dan mereka menjadi campur aduk I / O dan logika domain (logika domain berarti logika yang terkait dengan masalah aktual yang Anda coba selesaikan), program Anda bisa " kerja". Sasarannya, jika Anda berencana menjadikan ini lebih dari satu hal sederhana, harusnya berfungsi dengan baik , artinya Anda dapat mengubah bagian-bagian itu tanpa memengaruhi orang lain, perbaiki bug saat mereka muncul dan semoga memperpanjangnya tanpa terlalu banyak kesulitan ketika dan jika Anda menemukan fitur baru dan menggunakan kasing yang ingin Anda tambahkan.
OK, sekarang, jawabannya. Pertama: ya, menggunakan File sebagai parameter metode di Turbine
kelas melanggar SRP. Kelas Anda Turbine
dan Airfoil
seharusnya tidak tahu apa-apa tentang file. Dan, ya, ada cara yang lebih baik untuk melakukannya. Saya akan berbicara dengan Anda melalui satu cara saya akan melakukannya terlebih dahulu dan kemudian pergi ke lebih detail tentang mengapa lebih baik nanti. Ingat, ini hanya sebuah contoh (bukan kode yang benar-benar dapat dikompilasi, tetapi semacam pseudocode) dan salah satu cara yang mungkin untuk melakukannya.
// TurbineData struct (to hold the data for turbines)
struct TurbineData
{
int number_of_blades;
double hub_height;
}
// TurbineRepository (abstract) class
class TurbineRepository
{
// Defines an interface for Turbine repositories, which return Vectors of TurbineData structures.
public:
virtual std::Vector<TurbineData> getAll();
}
// TurbineFileRepository class
class TurbineFileRepository: public TurbineRepository
{
// Implements the TurbineRepository "interface".
public:
TurbineRepository(File inFile);
std::Vector<TurbineData> getAll();
private:
File file;
}
TurbineFileRepository::TurbineFileRepository(File inFile)
{
// Process the File and handle everything you need to read from it
// At some point, do something like:
// file = inFile
}
std::Vector<TurbineData> TurbineFileRepository::getAll()
{
// Get the data from the file here and return it as a Vector
}
// TurbineFactory class
class TurbineFactory
{
public:
TurbineFactory(TurbineRepository *repo);
std::Vector<Turbine> createTurbines();
private:
TurbineRepository *repository;
}
TurbineFactory::TurbineFactory(TurbineRepository *repo)
{
// Create the factory here and eventually do something like:
// repository = repo;
}
TurbineFactory::createTurbines()
{
// Create a new Turbine for each of the structs yielded by the repository
// Do something like...
std::Vector<Turbine> results;
for (auto const &data : repo->getAll())
{
results.push_back(Turbine(data.number_of_blades, data.hub_height));
}
return results;
}
// And finally, you would use it like:
int main()
{
TurbineFileRepository repo = TurbineFileRepository(/* your file here */);
TurbineFactory factory = TurbineFactory(&repo);
std::Vector<Turbines> my_turbines = factory.createTurbines();
// Do stuff with your newly created Turbines
}
OK, jadi, ide utama di sini adalah untuk mengisolasi, atau menyembunyikan, bagian-bagian berbeda dari program dari satu sama lain. Saya terutama ingin mengisolasi bagian inti dari program, di mana logika domain berada ( Turbine
kelas, yang sebenarnya memodelkan dan menyelesaikan masalah), dari detail lain, seperti penyimpanan. Pertama, saya mendefinisikan TurbineData
struktur untuk menyimpan data untuk Turbine
yang berasal dari dunia luar. Kemudian, saya mendeklarasikan TurbineRepository
kelas abstrak (berarti kelas yang tidak dapat dipakai, hanya digunakan sebagai induk untuk warisan) dengan metode virtual, yang pada dasarnya menggambarkan perilaku "menyediakan TurbineData
struktur dari dunia luar". Kelas abstrak ini juga bisa disebut antarmuka (deskripsi perilaku). The TurbineFileRepository
mengimplementasikan kelas yang metode (dan dengan demikian menyediakan perilaku yang) untukFile
s. Terakhir, TurbineFactory
penggunaan TurbineRepository
untuk mendapatkan TurbineData
struktur tersebut dan membuat Turbine
s:
TurbineFactory -> TurbineRepo -> Turbine // with TurbineData as a means of passing data.
Mengapa saya melakukannya dengan cara ini? Mengapa Anda memisahkan file I / O dari bagian dalam program Anda? Karena dua tujuan utama dari desain atau arsitektur program Anda adalah untuk mengurangi kompleksitas dan untuk mengisolasi perubahan. Mengurangi kerumitan berarti membuat segala sesederhana mungkin (tetapi tidak lebih sederhana) sehingga Anda dapat bernalar tentang bagian-bagian individual dengan benar dan terpisah: ketika Anda berpikir tentang Turbine
s, Anda seharusnya tidak memikirkan format di mana file yang berisi data turbin ditulis, atau apakah File
Anda sedang membaca ada atau tidak. Anda harus berpikir tentang Turbine
, titik.
Mengisolasi perubahan berarti bahwa perubahan harus memengaruhi jumlah tempat yang paling mungkin dalam kode, sehingga kemungkinan bug terjadi (dan area yang memungkinkan terjadinya bug setelah Anda mengubah kode) dikurangi hingga minimum absolut. Juga, hal-hal yang sering berubah, atau cenderung berubah di masa depan, harus terpisah dari hal-hal yang tidak. Dalam kasus Anda, misalnya, jika format penyimpanan Turbine
data dalam file berubah, seharusnya tidak ada alasan bagi Turbine
kelas untuk berubah, hanya kelas yang suka TurbineFileRepository
. Satu-satunya alasan Turbine
harus berubah adalah jika Anda menambahkan pemodelan yang lebih canggih ke dalamnya, atau fisika yang mendasarinya berubah (yang jauh lebih kecil kemungkinannya daripada perubahan format file), atau sesuatu yang serupa.
Rincian di mana dan bagaimana data disimpan harus ditangani secara terpisah oleh kelas, seperti TurbineFileRepository
, yang akan, akibatnya, tidak tahu tentang cara Turbine
kerjanya, atau bahkan mengapa data yang mereka berikan diperlukan. Kelas-kelas ini benar - benar harus menerapkan penanganan pengecualian I / O, dan semua jenis hal yang membosankan dan sangat penting yang terjadi ketika program Anda berbicara dengan dunia luar, tetapi mereka tidak boleh lebih dari itu. Fungsi TurbineRepository
adalah untuk menyembunyikan dari TurbineFactory
semua detail dan hanya menyediakannya dengan data vektor. Itu juga apa yang TurbineFileRepository
diterapkan sehingga tidak ada detail tentang hal itu perlu diketahui siapa pun yang ingin menggunakanTurbineData
struktur. Sebagai perubahan fitur yang bagus, bayangkan Anda ingin menyimpan data turbin dan airfoil dalam database MySQL. Agar itu berfungsi, yang perlu Anda lakukan hanyalah menerapkan TurbineDatabaseRepository
dan pasang. Tidak lebih. Keren ya
Semoga berhasil dengan pemrograman Anda!