Tek Sorumluluk modeli sınıflar için ne kadar spesifik olmalıdır?


14

Örneğin, konsoldan ve konsoldan her türlü giriş / çıkış yöntemine sahip bir konsol oyun programınız olduğunu varsayalım. Tek hepsini tutmak için akıllı olurdu inputOutputsınıf veya gibi daha spesifik sınıflara onları yıkmak startMenuIO, inGameIO, playerIO, gameBoardIO, her sınıf 1-5 yöntemleri hakkında sahip vb öyle ki?

Ve aynı notta, onları parçalamak daha iyi ise, onları bir IOad alanına yerleştirmek akıllıca olur, böylece onları biraz daha ayrıntılı olarak adlandırır, örneğin: IO.inGamevb.



İlgili: Girdi ve çıktıyı birleştirmek için hiç iyi bir neden görmedim. Ve onları kasten ayırdığımda, kodum çok daha temiz çıkıyor.
Mooing Duck

1
Neyse, lowerCamelCase'deki sınıflara hangi dilde ad veriyorsunuz?
user253751

Yanıtlar:


7

Güncelleme (özet)

Oldukça ayrıntılı bir cevap yazdığım için, işte bunların hepsi kaynıyor:

  • Ad alanları iyidir, mantıklı olduğunda kullanın
  • Kullanmak inGameIOve playerIOsınıflar muhtemelen SRP'nin ihlali anlamına gelecektir. Muhtemelen IO'yu kullanma şeklinizi uygulama mantığıyla birleştirdiğiniz anlamına gelir.
  • İşleyici sınıfları tarafından kullanılan (veya bazen paylaşılan) birkaç genel IO sınıfına sahip olun. Bu işleyici sınıfları daha sonra ham girdiyi uygulama mantığınızın anlayabileceği bir biçime dönüştürür.
  • Çıktı için de aynı şey geçerlidir: bu oldukça genel sınıflar tarafından yapılabilir, ancak oyun durumunu dahili oyun durumunu genel IO sınıflarının işleyebileceği bir işleyiciye dönüştüren bir işleyici / eşleştirici nesnesi aracılığıyla geçirin.

Sanırım buna yanlış bakıyorsunuz. Uygulamanın bileşenlerinin fonksiyonunda IO'yu ayırıyorsunuz, oysa - bana göre, IO'nun kaynağına ve "tipine" dayalı olarak ayrı IO sınıflarına sahip olmak daha mantıklı .

Başlamak için bazı temel / genel KeyboardIOsınıflara MouseIOsahip olmak ve daha sonra onlara ne zaman ve nerede ihtiyacınız olduğuna bağlı olarak, bahsedilen G / Ç'yi farklı şekilde işleyen alt sınıflara sahip olun.
Örneğin, metin girişi muhtemelen oyun içi denetimlerden farklı işlemek istediğiniz bir şeydir. Kendinizi, her kullanım durumuna bağlı olarak belirli anahtarları farklı şekilde eşlemek istediğinizde bulacaksınız, ancak bu eşleme IO'nun bir parçası değildir, bu IO'yu nasıl işlediğinizdir.

SRP'ye bağlı kaldığımda, klavye IO için kullanabileceğim birkaç dersim olurdu. Duruma bağlı olarak, muhtemelen bu sınıflarla farklı etkileşim kurmak isteyeceğim, ancak onların tek işi bana kullanıcının ne yaptığını anlatmak.

Daha sonra bu nesneleri ya uygulama mantığımın çalışabileceği bir şeye ham IO'yu eşleyecek bir işleyici nesnesine enjekte ederim (örneğin: kullanıcı "w" tuşuna basarsa , işleyici bu haritalara eşler MOVE_FORWARD).

Bu işleyiciler, karakterleri hareket ettirmek ve ekranı buna göre çizmek için kullanılır. Brüt aşırı basitleştirme, ancak özü bu tür bir yapıdır:

[ IO.Keyboard.InGame ] // generic, if SoC and SRP are strongly adhered to, changing this component should be fairly easy to do
   ||
   ==> [ Controls.Keyboard.InGameMapper ]

[ Game.Engine ] <- Controls.Keyboard.InGameMapper
                <- IO.Screen
                <- ... all sorts of stuff here
    InGameMapper.move() //returns MOVE_FORWARD or something
      ||
      ==> 1. Game.updateStuff();//do all the things you need to do to move the character in the given direction
          2. Game.Screen.SetState(GameState); //translate the game state (inverse handler)
          3. IO.Screen.draw();//generate actual output

Şimdi sahip olduğumuz şey, klavye IO'sundan ham haliyle sorumlu bir sınıf. Bu verileri oyun motorunun gerçekten anlayabileceği bir şeye dönüştüren başka bir sınıf, bu veriler daha sonra ilgili tüm bileşenlerin durumunu güncellemek için kullanılır ve son olarak, ayrı bir sınıf ekrana çıktı ile ilgilenir.

Her sınıfın tek bir işi vardır: klavye girişini işlemek, bilmediği / bakım yaptığı / işlediği girdinin ne anlama geldiğini bilmesi gereken bir sınıf tarafından yapılır. Tek yaptığı, girdinin nasıl alınacağını bilmektir (arabelleğe alınmış, arabelleğe alınmış, ...).

İşleyici bunu, uygulamanın geri kalanı için bu bilgileri anlaması için dahili bir temsile dönüştürür.

Oyun motoru çevrilen verileri alır ve bir şey olup olmadığını ilgili tüm bileşenlere bildirmek için kullanır. Bu bileşenlerin her biri, çarpışma kontrolleri veya karakter animasyonu değişiklikleri olsun, tek bir şey yapar, farketmez, bu her bir nesneye bağlıdır.

Bu nesneler daha sonra durumlarını geri aktarırlar ve bu veriler Game.Screenözünde ters bir IO işleyicisi olan iletilir . Dahili temsili, IO.Screenbileşenin gerçek çıktıyı oluşturmak için kullanabileceği bir şeyle eşleştirir .


Bir konsol uygulaması olduğundan fare yoktur ve mesajları veya kartı yazdırmak girişle sıkıca bağlantılıdır. Örneğin, IOve gamead alanları ya da alt sınıflı sınıflar mı?
shinzou

@kuhaku: Bunlar isim alanları. Söylediklerimin özü, uygulamanın hangi bölümünde bulunduğunuzu temel alarak alt sınıflar oluşturmayı seçerseniz, temel G / Ç işlevselliğini uygulama mantığınızla etkili bir şekilde birleştirirsiniz. Uygulamanın işlevinde IO'dan sorumlu sınıflarla sonuçlanacaksınız . Bana göre, SRP'nin ihlali gibi geliyor. İsimler ve isim
aralığı

Cevabımı özetlemek için cevabımı güncelledim
Elias Van Ootegem

Evet, bu aslında yaşadığım başka bir sorun (IO'yu mantık ile birleştirmek), bu yüzden aslında girdiyi çıktıdan ayırmanızı önerir misiniz?
shinzou

@kuhaku: Bu gerçekten ne yaptığınıza ve giriş / çıkış öğelerinin ne kadar karmaşık olduğuna bağlıdır. Eğer işleyicileri (ham giriş / çıkış vs oyun durumunu çeviri) çok fazla, o zaman muhtemelen tek IO sınıf gayet değilse, giriş ve çıkış sınıfları ayırmak isteyeceksiniz değişir
Elias Van Ootegem

23

Tek sorumluluk ilkesini anlamak zor olabilir. Yararlı bulduğum şey, cümleleri nasıl yazdığınızı düşünmektir. Çok fazla fikri tek bir cümleyle birleştirmeye çalışmıyorsunuz. Her cümle bir fikri açıkça belirtmeli ve ayrıntıları ertelemelidir. Örneğin, bir araba tanımlamak istiyorsanız, şunu söylerdiniz:

İçten yanmalı bir motorla çalışan, tipik olarak dört tekerlekli bir araç.

Sonra "araç", "yol", "tekerlekler" vb. Söylemeye çalışamazsınız:

İnsanları bir cadde, rota veya kara yolunda, taşıtın altına sabitlenmiş bir aks üzerinde dönen ve üreten bir motorla çalışan dört dairesel nesneye sahip, döşenmiş veya başka şekilde iyileştirilmiş iki yer arasındaki karada taşımak için bir araç benzin, yağ veya diğer yakıtların hava ile yakılmasıyla motor gücü.

Benzer şekilde, sınıflarınızı, yöntemlerinizi vb. Yapmaya çalışın, merkezi konsepti mümkün olduğunca basit bir şekilde belirtmeli ve ayrıntıları diğer yöntemlere ve sınıflara ertelemelisiniz. Tıpkı cümleler yazarken olduğu gibi, ne kadar büyük olmaları gerektiğine dair zor bir kural yoktur.


Yani otomobili birçok yerine birkaç kelimeyle tanımlamak gibi, küçük ama benzer sınıfları tanımlamakta fayda var mı?
shinzou

2
@kuhaku: Küçük iyidir. Spesifik iyidir. KURU tuttuğunuz sürece benzer. Tasarımınızı kolayca değiştirmeye çalışıyorsunuz. İşleri küçük ve spesifik tutmak, nerede değişiklik yapacağınızı bilmenize yardımcı olur. KURU tutmak, birçok yeri değiştirmek zorunda kalmamanıza yardımcı olur.
Vaughn Cato

6
İkinci cümleniz "Lanet olsun, öğretmen bunun 4 sayfa olması gerektiğini söyledi ..." itici güç üretir "öyle !!"
corsiKa

2

Söylemeliyim ki en iyi yol onları ayrı sınıflarda tutmak. Küçük sınıflar kötü değildir, aslında çoğu zaman iyi bir fikirdir.

Özel durumunuzla ilgili olarak, ayrılmış olmanın, bu belirli işleyicilerin herhangi birinin mantığını diğerlerini etkilemeden değiştirmenize yardımcı olabileceğini düşünüyorum ve eğer gerekiyorsa, yeni giriş / çıkış yöntemi eklemeniz daha kolay olacaktır.


0

Tek Sorumluluk Müdürü, bir sınıfın değişmesi için sadece bir sebebi olması gerektiğini belirtir . Sınıfınızın değiştirmek için birden fazla nedeni varsa, bunu diğer sınıflara ayırabilir ve bu sorunu ortadan kaldırmak için kompozisyondan yararlanabilirsiniz.

Sorunuzu cevaplamak için size bir soru sormam gerekiyor: Sınıfınızın değiştirmek için sadece bir nedeni var mı? Değilse, her birinin değişmesi için sadece bir nedeni olana kadar daha özel sınıflar eklemekten korkmayın.

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.