Büyük, sıkı bağlantılı sınıflar nasıl bölünür?


14

Ben biraz daha hafif ve temiz bir tasarım için mümkünse refactor istiyorum 2k satırdan fazla kod (ve büyüyen) bazı büyük sınıfları var.

Bu kadar büyük olmasının sebebi, bu sınıfların çoğu yöntemin erişmesi gereken bir dizi haritayı işlemesi ve yöntemlerin birbirine çok bağlı olmasıdır.

Çok somut bir örnek vereceğim: ServerGelen mesajları işleyen bir sınıfım var . Sanki yöntemleri vardır joinChatroom, searchUsers, sendPrivateMessage, bu yöntemlerin vb tüm haritalar manipüle gibi users, chatrooms, servers, ...

Belki de Chatrooms, Kullanıcılar hakkında başka bir işlem, vb ile ilgili bir sınıf taşıma mesajları olabilir güzel olurdu ama burada ana sorun yöntemlerin çoğunda tüm haritaları kullanmanız gerektiğidir. Bu yüzden şimdilik hepsi Serverbu ortak haritalara güvendikleri ve yöntemlerin birbirine çok bağlı olduğu için sınıfta kalıyorlar.

Bir sınıf Chatrooms oluşturmak gerekir, ama diğer nesnelerin her bir referans ile. Sınıf kullanıcıları, diğer tüm nesnelere referansla yeniden başvururlar.

Yanlış bir şey yapacağımı hissediyorum.


Kullanıcı ve Sohbet Odası gibi sınıflar yaparsanız, bu sınıfların yalnızca ortak veri yapısına referans vermeleri gerekir mi yoksa birbirlerine mi referans olurlar?

Burada birkaç tatmin edici cevap var, bir tane seçmelisiniz.
jeremyjjbrown

@jeremyjjbrown soru taşındı ve ben kaybettim. Bir cevap seçtiniz, teşekkürler.
Matthew

Yanıtlar:


10

Açıklamanızdan, haritalarınızın Serveryöntemlerin tüm mantığı ile tamamen veri torbaları olduğunu tahmin ediyorum . Tüm sohbet odası mantığını ayrı bir sınıfa iterek, yine de veri içeren haritalarla sıkışıp kalırsınız.

Bunun yerine, tek tek sohbet odalarını, kullanıcıları vb. Nesneleri modellemeye çalışın. Bu şekilde, büyük veri haritaları yerine yalnızca belirli bir yöntem için gereken belirli nesnelerin etrafından geçeceksiniz.

Örneğin:

public class User {
  private String name;
  ...

  public void sendMessage(String message) {
    ...
  }
}

public class Chatroom {
  // users in this chatroom
  private Collection<User> users;

  public void add(User user) {
    users.add(user);
  }

  public void sendMessage(String msg) {
    for (User user : users)
      user.sendMessage(msg);
  }
}

public class Server {
  // all users on the server
  private Collection<User> users;

  // all chatrooms on the server
  private Collection<Chatroom> chatrooms;

  /* methods to handle incoming messages */
}

Artık iletileri işlemek için birkaç belirli yöntemi aramak kolaydır:

Bir sohbet odasına katılmak ister misiniz?

chatroom.add(user);

Özel mesaj göndermek ister misiniz?

user.sendMessage(msg);

Herkese açık bir mesaj göndermek ister misiniz?

chatroom.sendMessage(msg);

5

Her koleksiyonu içeren bir sınıf oluşturabilmelisiniz. ServerBu koleksiyonların her birine bir referans gerekecek olsa da , sadece temel koleksiyonlara erişmeyi veya bunları korumayı içermeyen minimum miktarda mantık gerekir. Bu, Sunucunun ne yaptığını daha açık hale getirecek ve nasıl yaptığını ayrılacaktır.


4

Böyle büyük sınıflar gördüğümde, genellikle dışarı çıkmaya çalışan bir sınıf (ya da daha fazla) olduğunu gördüm. Bu sınıfla ilgili olmayabileceğini düşündüğünüz bir yöntem biliyorsanız, bunu statik hale getirin. Derleyici daha sonra size bu yöntemlerin çağırdığı diğer yöntemleri söyleyecektir. Java da statik olduklarında ısrar eder. Onları statik yaparsınız. Yine derleyici çağırdığı herhangi bir yöntemi söyleyecektir. Daha fazla derleme hatası kalmayana kadar bunu tekrar tekrar yapmaya devam edersiniz. Sonra büyük sınıfınızda bir sürü statik yöntem var. Şimdi bunları yeni bir sınıfa çekebilir ve yöntemi durağan hale getirebilirsiniz. Daha sonra bu yeni sınıfı orijinal büyük sınıfınızdan çağırabilirsiniz (artık daha az satır içermelidir)

Daha sonra sınıf tasarımından memnun olana kadar işlemi tekrarlayabilirsiniz.

Martin Fowler'in kitabı gerçekten iyi bir okuma, bu yüzden bu statik hileyi kullanamayacağınız zamanlar da tavsiye ederim.



1

Kodunuzun çoğu mevcut olduğundan, yöntemlerinizi taşımak için yardımcı sınıfları kullanmanızı öneririm. Bu kolay yeniden düzenlemeye yardımcı olacaktır. Yani, sunucu sınıfınız hala içindeki haritaları içerecektir. Ancak, Join (Map chatrooms, String user), List getUsers (Map chatrooms), Map getChatrooms (String user) gibi yöntemlerle ChatroomHelper diyen bir yardımcı sınıftan yararlanır.

Sunucu sınıfı, ChatroomHelper, UserHelper vb. Örneklerini içerecek ve böylece yöntemleri mantıksal yardımcı sınıflarına taşıyacaktır. Bu şekilde, genel yöntemleri sunucuda olduğu gibi bırakabilirsiniz, böylece herhangi bir arayanın değiştirilmesi gerekmez.


1

Kazablanka'nın içgörüsel cevabına eklemek için - birden fazla sınıfın bir tür varlıkla aynı temel şeyleri yapması gerekiyorsa (bir koleksiyona kullanıcı ekleme, iletileri işleme vb.), Bu süreçlerin de ayrı tutulması gerekir.

Bunu yapmanın birden çok yolu vardır - miras veya kompozisyon ile. Kalıtım için, soyut temel sınıfları, örneğin kullanıcıları veya iletileri işlemek için alanlar ve işlevsellik sağlayan somut yöntemlerle kullanabilirsiniz chatroomve userbu sınıfları ve (veya diğer) gibi varlıkları genişletebilirsiniz.

Çeşitli nedenlerle , kompozisyonu kalıtım üzerinde kullanmak iyi bir genel kuraldır. Bunu çeşitli şekillerde yapmak için kompozisyon kullanabilirsiniz. Kullanıcıların veya mesajların kullanımı, sınıfların sorumluluğunda merkezi işlevler olduğundan, kurucu enjeksiyonunun en uygun olduğu söylenebilir. Bu şekilde bağımlılık şeffaftır ve gerekli işleve sahip olmadan bir nesne oluşturulamaz. Kullanıcıların veya mesajların ele alınma şekli değişecekse veya genişletilecekse, strateji modeli gibi bir şey kullanmayı düşünmelisiniz .

Her iki durumda da, esneklik için somut sınıflara değil, arayüzlere kod yazdığınızdan emin olun.

Tüm söylenenler, bu tür kalıpları kullanırken her zaman ek karmaşıklığın maliyetini düşünün - buna ihtiyacınız olmayacaksa, kodlamayın. Büyük olasılıkla, kullanıcıların / mesajların işlenme şeklini değiştirmeyeceğinizi biliyorsanız, bir strateji modelinin yapısal karmaşıklığına ihtiyacınız yoktur - ancak endişeleri ayırmak ve tekrardan kaçınmak için ortak işlevselliği yine de boşaltmalısınız bunu kullanan somut örneklerden - ve, aksi yönde geçersiz kılma nedenleri yoksa, bu tür kullanım işlevselliğinin (sohbet odaları, kullanıcılar) kullanıcıları bu işlemi gerçekleştiren bir nesneyle oluşturun.

Özetlemek gerekirse:

  1. Casablanca'nın yazdığı gibi: Sohbet odalarını, kullanıcıları vb. Ayırın ve kapsülleyin.
  2. Ayrı ortak işlevsellik
  3. Veri sunumunu (erişim ve mutasyonun yanı sıra) boşanmak için bireysel işlevselliği, tek tek veri örnekleri veya toplamaları üzerinde daha karmaşık işlevsellikten ayırmayı düşünün (örneğin, searchUsersbir toplama sınıfında veya bir depoda / kimlik haritasında gidebilecek bir şey) )

0

Bakın, sorunuzun cevaplamak için çok genel olduğunu düşünüyorum, çünkü sorunun tam bir açıklamasına sahip değiliz, bu yüzden çok az bilgi ile iyi bir tasarım sunmak imkansız. Örnek olarak, daha iyi tasarımın olası yararsızlığına ilişkin endişelerinizden birini ele alabilirim.

Sunucu sınıfınızın ve gelecekteki Chatroom sınıfınızın kullanıcılar hakkındaki verileri paylaştığını söylersiniz, ancak bu veriler farklı olmalıdır. Sunucu muhtemelen kendisine bağlı bir dizi kullanıcıya sahipken, kendisi de bir Sunucuya ait olan bir Sohbet Odası'nın, yalnızca belirli bir Sohbet Odasına giriş yapmış kullanıcıların farklı bir kullanıcı kümesi, ilk grubun bir alt kümesi vardır.

Veri türleri aynı olsa bile bu aynı bilgi değildir.
İyi bir tasarımın birçok avantajı vardır.

Fowler'ın yukarıda bahsettiği kitabı henüz okumadım, ancak Folwer tarafından başka şeyler de okudum ve bana güvendiğim insanlar tarafından önerildi, bu yüzden diğerleriyle aynı fikirde olmak için yeterince rahat hissediyorum.


0

Haritalara erişim ihtiyacı mega sınıfı haklı çıkarmıyor. Birkaç sınıfta mantığı ayırmanız gerekir ve diğer sınıfların haritalara erişebilmesi için her sınıfın bir getMap yöntemi olmalıdır.


0

Başka bir yerde verdiğim yanıtı kullanırdım : yekpare sınıfı alın ve sorumluluklarını diğer sınıflara ayırın. Hem DCI hem de Ziyaretçi Kalıbı bunu yapmak için iyi seçenekler sunar.


-1

Yazılım metriği açısından büyük sınıf çantadır. Bu ifadeyi kanıtlayan sınırsız makaleler var. Neden ? çünkü büyük sınıfların anlaşılması küçük sınıflardan daha zordur ve değiştirilmeleri için daha fazla zamana ihtiyaç vardır. Dahası, test yaparken Büyük sınıflar çok zordur. Ve yeniden kullanmak istediğinizde büyük sınıflar sizin için çok zordur çünkü büyük olasılıkla istenmeyen şeyler içerir.

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.