MVC: Kontrolör Tek Sorumluluk İlkesini ihlal ediyor mu?


16

Tek Sorumluluk İlkesi "bir sınıfın değişimin bir nedeni olması gerektiğini" belirtir.

MVC modelinde, Kontrolcünün işi Görünüm ve Model arasında aracılık etmektir. Kullanıcı tarafından GUI üzerinde yapılan eylemleri rapor etmek için Görünüm için bir arayüz sunar (örneğin, Görünüm'ün çağrı yapmasına izin vermek controller.specificButtonPressed()) ve verilerini değiştirmek veya işlemlerini başlatmak için Model üzerinde uygun yöntemleri çağırabilir (örn. model.doSomething()) .

Bunun anlamı şudur ki:

  • Denetleyiciye, kullanıcı işlemlerini bildirmek üzere uygun bir arabirim göstermesi için GUI hakkında bilgi sahibi olması gerekir.
  • Ayrıca Model üzerinde uygun yöntemleri çağırabilmek için Modeldeki mantık hakkında bilgi sahibi olmak gerekir.

Bunun iki değişik nedeni olduğu anlamına gelir : GUI'de bir değişiklik ve iş mantığında bir değişiklik.

GUI değişirse, örneğin yeni bir düğme eklenirse, Kontrolörün Görünüm'ün bir kullanıcıyı rapor etmesine izin vermek için yeni bir yöntem eklemesi gerekebilir.

Modeldeki iş mantığı değişirse, Model üzerinde doğru yöntemleri çağırmak için Kontrolörün değişmesi gerekebilir.

Bu nedenle, Denetleyicinin değiştirmek için iki olası nedeni vardır . SRP'yi bozuyor mu?


2
Denetleyici 2 yönlü bir sokaktır, tipik yukarıdan aşağıya veya aşağıdan yukarıya yaklaşımınız değildir. Bağımlılıklarından birini soyutlama imkanı yoktur, çünkü kontrolör soyutlamanın kendisidir. Desenin doğası nedeniyle burada SRP'ye bağlı kalmak mümkün değildir. Kısacası: evet, SRP'yi ihlal ediyor ama bu kaçınılmaz.
Jeroen Vannevel

1
Sorunun amacı nedir? Hepimiz "evet, öyle" diye cevap verirsek, ne olacak? Ya cevap "hayır" ise? Bu soru ile çözmeye çalıştığınız gerçek sorun nedir?
Bryan Oakley

1
"değiştirmek için bir neden", "değişen kod" anlamına gelmez. Sınıf için değişken adlarınızda yedi yazım hatası yaparsanız, bu sınıfın artık 7 sorumluluğu var mı? Hayır. Birden fazla değişkeniniz veya birden fazla işleviniz varsa, yine de yalnızca tek bir sorumluluğunuz olabilir.
Bob

Yanıtlar:


14

Sonuç olarak SRP ile ilgili akıl yürütmeye devam ederseniz, "tek sorumluluk" un aslında süngerimsi bir terim olduğunu fark edeceksiniz. İnsan beynimiz bir şekilde farklı sorumlulukları ayırt edebiliyor ve çoklu sorumluluklar bir "genel" sorumluluk olarak soyutlanabiliyor. Örneğin, 4 kişilik ortak bir ailede kahvaltı yapmaktan sorumlu bir aile üyesi olduğunu düşünün. Şimdi, bunu yapmak için, yumurta ve tost ekmeği kaynatmalı ve tabii ki sağlıklı bir fincan yeşil çay kurmalı (evet, yeşil çay en iyisidir). Bu şekilde "kahvaltı yapmak", birlikte "kahvaltı yapmak" için soyutlanan küçük parçalara bölebilirsiniz. Her bir parçanın, örneğin başka birine devredilebilecek bir sorumluluk olduğuna dikkat edin.

MVC'ye dönersek: model ve görüş arasında aracılık yapmak bir sorumluluk değil iki sorumluluktursa, o zaman bu ikisini birleştiren yukarıdaki soyutlama katmanı ne olurdu? Birini bulamazsanız, ya doğru bir şekilde soyutlamadınız ya da hiçbir şey yoktur, bu da onu tamamladığınız anlamına gelir. Ve bir kontrolörde, bir görünümde ve bir modelde durum böyle hissediyorum.


1
Tam not olarak. Controller.makeBreakfast () ve controller.wakeUpFamily () varsa, bu denetleyici SRP'yi kırar, ancak birden fazla sorumluluğu olduğu için bir denetleyici olduğu için değil.
Bob

Cevapladığınız için teşekkürler, sizi takip ettiğimden emin değilim. Denetleyicinin birden fazla sorumluluğu olduğunu kabul ediyor musunuz? Bence bunun sebebi değişmenin iki sebebi olması. Buna katılıyor musun?
Aviv Cohn

1
Evet, denetleyicinin birden fazla sorumluluğu olduğunu kabul edebilirim. Ancak bunlar "daha düşük" sorumluluklardır ve bu bir sorun değildir, çünkü en yüksek soyutlama düzeyinde sadece bir sorumluluğu vardır (bahsettiğiniz daha düşük olanları birleştirerek) ve bu yüzden SRP'yi ihlal etmez.
valenterri

1
Doğru soyutlama seviyesini bulmak kesinlikle önemlidir. "Kahvaltı yap" örneği iyi bir örnektir - tek bir sorumluluğu yerine getirmek için genellikle yapılması gereken bazı görevler vardır. Denetleyici yalnızca bu görevleri düzenlediği sürece SRP'yi izler. Ancak yumurta kaynatmak, tost yapmak veya çay demlemek hakkında çok fazla şey biliyorsa, SRP'yi ihlal eder.
Allan

Bu cevap bana mantıklı geliyor. Teşekkür ederim Valenterry.
J86

9

Bir sınıfın "değiştirmek için iki nedeni" varsa, evet, SRP'yi ihlal eder.

Bir denetleyici genellikle hafif olmalı ve GUI tarafından yönlendirilen bazı olaylara yanıt olarak etki alanını / modeli manipüle etme konusunda tek sorumluluğa sahip olmalıdır. Bu manipülasyonların her birini temel olarak kullanım örnekleri veya özellikler olarak düşünebiliriz.

GUI'ye yeni bir düğme eklenirse, denetleyici yalnızca bu yeni düğme bazı yeni bir özelliği temsil ediyorsa (yani ekran 1'de var olan ancak henüz ekran 2'de mevcut olmayan aynı düğmenin aksine) değişmelidir. ekran 2'ye eklendi). Bu yeni işlevselliği / özelliği desteklemek için modelde buna karşılık gelen yeni bir değişiklik olması gerekecektir. Denetleyici hala GUI tarafından yönlendirilen bazı olaylara yanıt olarak etki alanını / modeli değiştirme sorumluluğuna sahiptir.

Modeldeki iş mantığı bir hatanın düzeltilmesi nedeniyle değişiyorsa ve denetleyicinin değişmesini gerektiriyorsa, bu özel bir durumdur (veya belki de model açık-kapalı prensibi ihlal etmektedir). Modeldeki iş mantığı bazı yeni işlevleri / özellikleri destekleyecek şekilde değişirse, denetleyiciyi mutlaka etkilemez - yalnızca denetleyicinin bu özelliği göstermesi gerekiyorsa (neredeyse her zaman böyle olur, aksi halde neden eklenir? kullanılmayacaksa alan modeli). Bu nedenle, bu durumda denetleyici, bazı GUI güdümlü olaylara yanıt olarak etki alanı modelinin bu yeni şekilde değiştirilmesini desteklemek için de değiştirilmelidir.

Denetleyicinin değişmesi gerekiyorsa, örneğin, kalıcılık katmanı düz bir dosyadan veritabanına değiştirildiğinde, denetleyici kesinlikle SRP'yi ihlal ediyor demektir. Denetleyici her zaman aynı soyutlama katmanında çalışıyorsa, bu SRP'ye ulaşmaya yardımcı olabilir.


4

Denetleyici SRP'yi ihlal etmiyor. Belirttiğiniz gibi, sorumluluğu modeller ve görüş arasında arabuluculuk yapmaktır.

Bununla birlikte, örneğinizle ilgili sorun, görünümde mantık için denetleyici yöntemleri bağlamanızdır controller.specificButtonPressed. Yöntemleri bu şekilde adlandırmak, denetleyiciyi GUI'nize bağlar, işleri düzgün bir şekilde soyutlayamadınız. Kontrolör belirli eylemler gerçekleştirmeyle ilgili olmalıdır, yani controller.saveDataveya controller.retrieveEntry. GUI'ye yeni bir düğme eklemek, denetleyiciye yeni bir yöntem eklemek anlamına gelmez.

Görünümde bir düğmeye basmak, bir şey yapmak anlamına gelir, ancak ne olursa olsun, başka herhangi bir sayıda kolayca tetiklenebilir veya hatta görünüm yoluyla değil.

SRP hakkındaki Wikipedia makalesinden

Martin bir sorumluluğu değişim nedeni olarak tanımlar ve bir sınıfın ya da modülün değiştirmek için sadece bir tane nedeni olması gerektiği sonucuna varır. Örnek olarak, bir raporu derleyen ve yazdıran bir modülü ele alalım. Böyle bir modül iki nedenden dolayı değiştirilebilir. İlk olarak, raporun içeriği değişebilir. İkinci olarak, raporun biçimi değişebilir. Bu iki şey çok farklı nedenlerle değişir; bir önemli ve bir kozmetik. Tek sorumluluk ilkesi, sorunun bu iki yönünün gerçekten iki ayrı sorumluluk olduğunu ve bu nedenle ayrı sınıflarda veya modüllerde olması gerektiğini söylüyor. Farklı zamanlarda farklı nedenlerle değişen iki şeyi birleştirmek kötü bir tasarım olurdu.

Denetleyici, yalnızca yöntemlerinden biri çağrıldığında görünüme belirtilen verileri sağlamasıyla ilgili görünümde olanlarla ilgilenmez. Sadece modelin işlevselliğini, sahip olacakları yöntemleri çağırması gerektiğini bildiği kadarıyla bilmelidir. Bundan daha fazlasını bilmiyor.

Bir nesnenin çağrılabilecek bir yöntemi olduğunu bilmek, işlevselliğini bilmekle aynı şey değildir.


1
Denetleyicinin gibi yöntemler içermesi gerektiğini düşündüğümün nedeni specificButtonsPressed(), görünümün düğmelerinin ve diğer GUI öğelerinin işlevselliği hakkında hiçbir şey bilmemesi gerektiğidir. Bir düğmeye basıldığında, görünümün yalnızca denetleyiciye rapor etmesi ve denetleyicinin 'ne anlama geldiğine' karar vermesi (ve sonra modelde uygun yöntemleri çağırması) öğretildi. Görüntüleme araması yapmak, görünümün controller.saveData()bu düğmeye basılmasının yanı sıra bu düğmeye basmanın ne anlama geldiğini bilmesi gerektiği anlamına gelir.
Aviv Cohn

1
Bir tuşa basma ve bunun anlamı (denetleyicinin gibi yöntemlere sahip olmasıyla sonuçlanır) arasında tam bir ayırma fikrini terk edersem specificButtonPressed(), gerçekten de denetleyici GUI'ye çok fazla bağlı olmaz. specificButtonPressed()Yöntemleri atmalı mıyım ? Bu yöntemlere sahip olmanın avantajı size mantıklı geliyor mu? Yoksa buttonPressed()kontrolörde yöntemlere sahip olmak zahmete değmez mi?
Aviv Cohn

1
Diğer bir deyişle (üzgün uzun yorumlar için) olarak: Ben düşünüyorum sahip olmanın avantajı o specificButtonPressed()denetleyici yöntemleri, bu 's tuşa basar anlamı View from ayıran olmasıdır tamamen . Bununla birlikte, dezavantajı, denetleyiciyi bir anlamda GUI'ye bağlamasıdır. Hangi yaklaşım daha iyi?
Aviv Cohn

@prog IMO Denetleyici görünüme kör olmalıdır. Bir düğmenin bir tür işlevselliği olacaktır, ancak ayrıntılarını bilmesine gerek yoktur. Sadece bir denetleyici yöntemine bazı veriler gönderdiğini bilmelidir. Bu bakımdan isim önemli değil. fooKolayca çağrılabilir fireZeMissiles. Sadece belirli bir fonksiyona rapor vermesi gerektiğini bilecektir. Fonksiyonun sadece onu çağıracak ne yaptığını bilmiyor. Kontrolör, yöntemlerinin nasıl çağrıldığıyla ilgilenmez, sadece belli bir şekilde yanıt verir.
Schleis

1

Bir kontrolörün tek sorumluluğu, görüş ve model arasında aracılık eden sözleşme olmaktır. Görünüm yalnızca ekrandan sorumlu olmalı, model sadece iş mantığından sorumlu olmalıdır. Bu iki sorumluluğu kapatmak denetçilerin sorumluluğundadır.

Her şey yolunda ve iyi ama biraz akademiden uzak durmak; MVC'deki bir kontrolör genellikle birçok küçük eylem yönteminden oluşur. Bu eylemler genellikle bir şeyin yapabileceği şeylere karşılık gelir. Ürün satıyorsam, muhtemelen bir ProductController'ım olacak. Bu denetleyicinin GetReviews, ShowSpecs, AddToCart vb gibi eylemleri olacaktır ...

Görünüm, kullanıcı arayüzünü görüntüleme SRP'sine sahiptir ve bu kullanıcı arayüzünün bir kısmı AddToCart yazan bir düğme içerir.

Denetleyici, sürece dahil olan tüm Görünümleri ve Modelleri bilen SRP'ye sahiptir.

Denetleyiciler AddToCart Action, bir ürün sepete eklendiğinde dahil edilmesi gereken herkesi tanımak için belirli bir SRP'ye sahiptir.

Ürün Modelinde ürün mantığını modelleme SRP'si ve ShoppingCart Modelinde öğelerin daha sonra çıkış için nasıl kaydedileceği modelleme SRP'si bulunur. Kullanıcı Modelinde, kullanıcılara sepetlerine bir şeyler ekleyen bir modelleme SRP'si verilir.

İşletmenizi gerçekleştirmek için modelleri yeniden kullanabilir ve kullanmalısınız ve bu modellerin kodunuzun bir noktasında birleştirilmesi gerekir. Kontrolör, kuplajın gerçekleştiği her bir benzersiz yolu kontrol eder.


0

Denetleyicilerin aslında tek bir sorumluluğu vardır: uygulama durumunu kullanıcının girdisine göre değiştirmek.

Bir denetleyici , modelin durumunu güncellemek için modele komutlar gönderebilir (örneğin, bir belgeyi düzenleme). Ayrıca görünümün modelin sunumunu değiştirmek için ilişkili görünüme komutlar gönderebilir (örneğin, bir belgeyi kaydırarak).

source: wikipedia

Bunun yerine Rails tarzı "denetleyicileri" (etkin kayıt örnekleri ve aptal şablonlar hokkabazlık) varsa , o zaman elbette SRP kırıyor.

Daha sonra, Rails tarzı uygulamalar gerçekten MVC değildir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.