Saya sedang menulis aplikasi yang akan memiliki Image
entitas, dan saya sudah mengalami kesulitan dalam menentukan tanggung jawab masing-masing tugas.
Pertama saya punya Image
kelas. Ini memiliki jalur, lebar, dan atribut lainnya.
Lalu saya membuat sebuah ImageRepository
kelas, untuk mengambil gambar dengan metode tunggal dan diuji, misalnya: findAllImagesWithoutThumbnail()
.
Tapi sekarang aku juga harus bisa createThumbnail()
. Siapa yang harus menghadapinya? Saya berpikir tentang memiliki ImageManager
kelas, yang akan menjadi kelas khusus aplikasi (juga akan ada manipulasi gambar pihak ketiga komponen pilihan yang dapat digunakan kembali, saya tidak menciptakan kembali roda).
Atau mungkin 0K untuk membiarkan Image
ukurannya sendiri? Atau biarkan ImageRepository
dan ImageManager
menjadi kelas yang sama?
Bagaimana menurut anda?
sumber
Jawaban:
Pertanyaan yang diajukan terlalu samar untuk memiliki jawaban nyata karena sangat tergantung pada bagaimana
Image
benda akan digunakan.Jika Anda hanya menggunakan gambar pada satu ukuran, dan mengubah ukuran karena gambar sumber adalah ukuran yang salah, mungkin yang terbaik adalah meminta kode baca melakukan pengubahan ukuran. Mintalah
createImage
metode Anda mengambil lebar / tinggi dan kemudian mengembalikan gambar yang telah diubah ukurannya pada lebar / tinggi itu.Jika Anda membutuhkan beberapa ukuran, dan jika memori tidak menjadi masalah, yang terbaik adalah menyimpan gambar dalam memori seperti yang dibaca semula dan melakukan pengubahan ukuran pada waktu tampilan. Dalam kasus seperti itu, ada beberapa desain berbeda yang mungkin Anda gunakan. Gambar
width
danheight
akan diperbaiki, tetapi Anda akan memilikidisplay
metode yang mengambil posisi dan tinggi target / lebar atau Anda akan memiliki beberapa metode mengambil lebar / tinggi yang mengembalikan objek yang apa pun sistem tampilan yang Anda gunakan. Sebagian besar API gambar yang saya gunakan memungkinkan Anda menentukan ukuran target pada waktu menggambar.Jika persyaratan yang menyebabkan gambar pada ukuran yang berbeda cukup sering untuk kinerja menjadi perhatian, Anda dapat memiliki metode yang membuat gambar baru dengan ukuran berbeda berdasarkan aslinya. Alternatif lain adalah membuat
Image
representasi cache kelas Anda berbeda secara internal sehingga pertama kali Anda menelepondisplay
dengan ukuran thumbnail itu mengubah ukuran sementara yang kedua hanya menggambar salinan cache yang disimpan dari waktu lalu. Ini menggunakan lebih banyak memori, tetapi jarang Anda memiliki lebih dari beberapa resizings umum.Alternatif lain adalah memiliki satu
Image
kelas yang mempertahankan gambar dasar dan memiliki kelas yang berisi satu atau lebih representasi. AnImage
itu sendiri tidak akan memiliki tinggi / lebar. Sebaliknya, itu akan mulai denganImageRepresentation
yang memiliki tinggi dan lebar. Anda akan menggambar representasi ini. Untuk "mengubah ukuran" gambar, Anda akan memintaImage
representasi dengan metrik tinggi / lebar tertentu. Ini akan menyebabkannya memuat representasi baru ini serta yang asli. Ini memberi Anda banyak kontrol atas apa yang berkeliaran di memori dengan biaya kompleksitas ekstra.Saya pribadi tidak suka kelas yang mengandung kata
Manager
karena "manajer" adalah kata yang sangat samar yang tidak benar-benar memberi tahu Anda banyak tentang apa yang dilakukan kelas. Apakah itu mengatur objek seumur hidup? Apakah itu berdiri di antara sisa aplikasi dan hal yang dikelola?sumber
Buat segala sesuatunya sederhana selama hanya ada beberapa persyaratan, dan tingkatkan desain Anda ketika Anda harus. Saya kira dalam kebanyakan kasus dunia nyata tidak ada yang salah untuk memulai dengan desain seperti itu:
Hal-hal mungkin menjadi berbeda ketika Anda perlu memberikan lebih banyak parameter
createThumbnail()
, dan parameter-parameter itu membutuhkan masa pakai sendiri. Misalnya, mari kita asumsikan Anda akan membuat thumbnail untuk beberapa ratus gambar, semua dengan ukuran tujuan tertentu, algoritma ukuran atau kualitas. Itu berarti bahwa Anda dapat memindahkancreateThumbnail
kelas lain, misalnya, kelas manajer, atauImageResizer
kelas, di mana parameter tersebut dilewatkan oleh konstruktor dan terikat dengan masa pakaiImageResizer
objek.Sebenarnya, saya akan mulai dengan pendekatan pertama dan refactor nanti ketika saya benar-benar membutuhkannya.
sumber
ImageResizer
kelas? Kapan Anda mendelegasikan panggilan alih-alih memindahkan tanggung jawab ke kelas baru?Image thumbnail = img.createThumbnail(x,y)
.Saya pikir itu harus menjadi bagian dari
Image
kelas, karena mengubah ukuran di kelas eksternal akan membutuhkan pengubah untuk mengetahui implementasiImage
, sehingga melanggar enkapsulasi. Saya berasumsiImage
adalah kelas dasar, dan Anda akan berakhir dengan subclass terpisah untuk tipe gambar konkret (PNG, JPEG, SVG, dll). Oleh karena itu, Anda harus memiliki kelas ukuran yang sesuai, atau resizer generik denganswitch
pernyataan yang mengubah ukuran berdasarkan kelas implementasi - bau desain klasik.Salah satu pendekatan mungkin membuat
Image
konstruktor Anda mengambil sumber daya yang berisi gambar, dan parameter tinggi dan lebar dan membuatnya sendiri dengan tepat. Kemudian mengubah ukuran bisa sesederhana membuat objek baru menggunakan sumber asli (di-cache di dalam gambar) dan parameter ukuran baru. Misalnyafoo.createThumbnail()
akan sederhanareturn new Image(this.source, 250, 250)
. (denganImage
menjadi tipe konkretfoo
, tentu saja). Ini membuat gambar Anda tidak berubah dan implementasinya pribadi.sumber
Image
. Yang dibutuhkan hanyalah sumber dan dimensi target.Saya tahu bahwa OOP adalah tentang merangkum data dan perilaku bersama-sama, tetapi saya tidak berpikir itu ide yang baik untuk Gambar untuk memiliki ukuran logika yang tertanam dalam kasus ini, karena Gambar tidak perlu tahu bagaimana mengubah ukuran dirinya menjadi sebuah Gambar.
Thumbnail sebenarnya adalah Gambar yang berbeda. Mungkin Anda memiliki struktur data yang menahan hubungan antara sebuah Foto dan Gambar Kecil (keduanya adalah Gambar).
Saya mencoba membagi program saya menjadi beberapa hal (seperti Gambar, Foto, Gambar Kecil, dll.) Dan Layanan (seperti PhotographRepository, ThumbnailGenerator, dll.). Dapatkan struktur data Anda dengan benar, dan kemudian tentukan layanan yang memungkinkan Anda membuat, memanipulasi, mengubah, bertahan, dan memulihkan struktur data tersebut. Saya tidak menaruh perilaku lagi dalam struktur data saya selain memastikan mereka dibuat dengan benar dan digunakan dengan tepat.
Karena itu, tidak, sebuah Gambar tidak boleh berisi logika tentang cara membuat Gambar Kecil. Harus ada layanan ThumbnailGenerator yang memiliki metode seperti:
Struktur data saya yang lebih besar mungkin terlihat seperti ini:
Tentu saja itu mungkin berarti Anda melakukan upaya yang tidak ingin Anda lakukan saat membangun objek, jadi saya akan mempertimbangkan sesuatu seperti ini juga OK:
... dalam kasus di mana Anda menginginkan struktur data dengan evaluasi malas. (Maaf saya tidak menyertakan cek nol saya dan saya tidak membuatnya aman, yang merupakan sesuatu yang Anda inginkan jika Anda mencoba meniru struktur data yang tidak dapat diubah).
Seperti yang Anda lihat, salah satu dari kelas-kelas ini sedang dibangun oleh semacam PhotographRepository, yang mungkin memiliki referensi ke ThumbnailGenerator yang didapat melalui injeksi dependensi.
sumber
Anda telah mengidentifikasi satu fungsi yang ingin Anda terapkan, jadi mengapa tidak terpisah dari semua yang telah Anda identifikasi sejauh ini? Itulah yang disarankan oleh Prinsip Tanggung Jawab Tunggal sebagai solusinya.
Buat
IImageResizer
antarmuka yang memungkinkan Anda mengirimkan gambar dan ukuran target, dan yang mengembalikan gambar baru. Kemudian buat implementasi dari antarmuka itu. Sebenarnya ada banyak cara untuk mengubah ukuran gambar, sehingga Anda bahkan bisa menghasilkan lebih dari satu!sumber
Saya berasumsi fakta-fakta tentang metode, yang mengubah ukuran gambar:
Berdasarkan fakta-fakta itu, saya akan mengatakan bahwa tidak ada alasan untuk metode mengubah ukuran gambar menjadi bagian dari kelas Image itu sendiri. Menerapkannya sebagai metode pembantu statis kelas akan menjadi yang terbaik.
sumber
Kelas Pemrosesan Gambar mungkin sesuai (atau Pengelola Gambar, seperti Anda menyebutnya). Lewati gambar Anda ke metode CreateThumbnail dari Prosesor Gambar, misalnya, untuk mengambil gambar thumbnail.
Salah satu alasan saya menyarankan rute ini adalah karena Anda mengatakan Anda menggunakan pustaka pemrosesan gambar pihak ketiga. Mengambil fungsi pengubahan ukuran dari kelas Image itu sendiri mungkin membuatnya lebih mudah bagi Anda untuk mengisolasi setiap platform spesifik atau kode pihak ke-3. Jadi, jika Anda dapat menggunakan kelas Gambar dasar di semua platform / aplikasi, maka Anda tidak perlu mencemarinya dengan platform atau kode khusus perpustakaan. Itu semua dapat ditemukan di Image Processor.
sumber
Pada dasarnya seperti yang dikatakan Doc Brown:
Buat
getAsThumbnail()
metode untuk kelas gambar, tetapi metode ini seharusnya hanya mendelegasikan pekerjaan ke beberapaImageUtils
kelas. Jadi akan terlihat seperti ini:Dan
Ini akan memungkinkan kode yang lebih mudah dilihat. Bandingkan yang berikut ini:
Atau
Jika yang terakhir tampaknya baik untuk Anda, Anda juga dapat tetap membuat metode pembantu ini di suatu tempat.
sumber
Saya pikir dalam "Domain Gambar", Anda hanya memiliki objek Gambar yang tidak berubah dan monadik. Anda meminta gambar untuk versi yang diubah ukurannya dan mengembalikan versi ukurannya sendiri. Kemudian Anda dapat memutuskan apakah Anda ingin menyingkirkan yang asli atau menyimpan keduanya.
Sekarang versi thumbnail, avatar, dll dari Gambar adalah sepenuhnya Domain lain, yang dapat meminta domain Gambar untuk versi berbeda dari gambar tertentu untuk diberikan kepada pengguna. Biasanya domain ini juga tidak terlalu besar atau generik, jadi Anda mungkin dapat menyimpannya dalam logika aplikasi.
Dalam aplikasi skala kecil, saya akan mengubah ukuran gambar pada waktu baca. Misalnya saya bisa memiliki aturan apache penulisan ulang yang mendelegasikan ke php script jika gambar 'http://my.site.com/images/thumbnails/image1.png', di mana file akan diambil menggunakan nama image1.png dan diubah ukurannya dan disimpan dalam 'thumbnails / image1.png'. Kemudian pada permintaan berikutnya untuk gambar yang sama ini, apache akan melayani gambar secara langsung tanpa menjalankan skrip php. Pertanyaan Anda tentang findAllImagesWithoutThumbnails secara otomatis dijawab oleh apache, kecuali Anda perlu melakukan statistik?
Dalam aplikasi berskala besar, saya akan mengirim semua gambar baru ke pekerjaan latar belakang, yang menangani pembuatan berbagai versi gambar dan menyimpannya di tempat yang tepat. Saya tidak akan repot-repot membuat seluruh domain atau kelas karena domain ini sangat tidak mungkin untuk tumbuh menjadi berantakan spageti dan saus yang buruk.
sumber
Jawaban singkat:
Rekomendasi saya adalah menambahkan metode ini ke kelas gambar:
Objek Gambar masih tidak dapat diubah, metode ini mengembalikan gambar baru.
sumber
Ada beberapa jawaban bagus sudah, jadi saya akan menguraikan sedikit tentang heuristik di balik cara mengidentifikasi objek dan tanggung jawab mereka.
OOP berbeda dari kehidupan nyata karena benda-benda dalam kehidupan nyata seringkali pasif, dan dalam OOP mereka aktif. Dan ini adalah inti dari pemikiran objek . Misalnya, siapa yang akan mengubah ukuran gambar di kehidupan nyata? Seorang manusia, yang cerdas dalam hal itu. Tapi di OOP tidak ada manusia, jadi objeknya pintar. Cara untuk menerapkan pendekatan "human-centric" ini dalam OOP adalah memanfaatkan kelas layanan, misalnya,
Manager
kelas-kelas terkenal . Dengan demikian objek diperlakukan sebagai potongan data pasif. Ini bukan cara OOP.Jadi ada dua opsi. Yang pertama, membuat metode
Image::createThumbnail()
, sudah dipertimbangkan. Yang kedua adalah membuatResizedImage
kelas. Ini bisa menjadi dekorator dariImage
(tergantung pada domain Anda apakah akan mempertahankanImage
antarmuka atau tidak), meskipun itu menghasilkan beberapa masalah enkapsulasi, karenaResizedImage
harus memilikiImage
sumber. TetapiImage
tidak akan kewalahan dengan mengubah ukuran detail, meninggalkannya ke objek domain terpisah, bertindak sesuai dengan SRP.sumber