2009-07-01 16 views
5
toplama sınıfları enkapsüle

varsayalım ben aşağıdaki veri türleri vardır:devralma ve Java

class Customer { 
    String id; // unique 
    OtherCustData someOtherData; 
} 

class Service { 
    String url; // unique 
    OtherServiceData someOtherData; 
} 

class LastConnection { 
    Date date; 
    OtherConnData someOtherData; // like request or response 
} 

Şimdi zaman hizmetlerin her biri bağlı müşterilerinin her hatırlamak gerekiyor.
Ben yapısı oluşturur:

Map<Customer, Map<Service, LastConnection>> lastConnections; 

Veya, kimlikleri ile arama yapabilmek için ve tüm eşit yazmak zorunda() ve hashCode():

Map<String, Map<String, LastConnection>> lastConnections; 

Şimdi erişebilecek

LastConnection connection = lastConnections.get(custId).get(srvUrl); 

Bütün bunlar ben LastConnections, s haritaların harita bekliyor yöntemlerinin onlarca parametre olarak geçmek zorunda özellikle de çirkin görünüyor tarafından LastConnection verileri o ben böyle bir şey olmazdı ki kendi sınıfları oluşturma düşünüyorum:

class CustomerConnections { 
    Map<String, LastConnection> customerConnections; 
    LastConnection get(String srvUrl) { 
     return customerConnections.get(srvUrl); 
    } 
    ... // all other needed operations; 
} 

class AllConnections { 
    Map<String, CustomerConnections> allConnections; 
    public LastConnection get(String custId, String srvUrl) { 
     return get(custId).get(srvUrl); 
    } 
    public CustomerConnection get(String custId) { 
     return allConnections.get(custId); 
    } 
    ... // all other needed operations; 
} 

sorun şudur:

class CustomerConnections extends HashMap<String, LastConnection> { 
} 

class AllConnections extends HashMap<String, CustomerConnections> { 
    public LastConnection get(String custId, String srvUrl) { 
     return get(custId).get(srvUrl); 
    } 
} 

Tamam, o miras 3v1l zaten öğrenmiş, bu yüzden kompozisyon deneyelim KATIL prensiplere ve en iyi uygulamalara saygı duyan en iyi yaklaşımın ne olacağından emin değilim. Halihazırda var olan koleksiyonları genişletme dışında hiçbir şey yapmayan sınıflar oluşturmak, varlıkları gerekliliklerin ötesinde çoğaltmak gibi görünüyor, fakat benim kodumu daha açık hale getirecektir (Özellikle de bir sonraki seviyeler olduğunda - AllConnections'ın Haritası ay ve benzeri gibi). Herhangi bir yön?

cevap

5

için temsilci kısa bir şey yapmak yöntemlerden çok uygulamaya gerekliliğinde olumsuz var zaten mevcut koleksiyonlarını uzanan gerekliliği

Ben saklanması uzatmak değiştirecek

ötesinde varlıkları çarparak gibi görünüyor. Bu bilgilerin nasıl saklandığına dair detayları saklıyorsunuz. Sınıfınızın müşterileri, Müşterinin bağlantı geçmişini nasıl sağladığınızı bilmeye gerek duymaz. Bence bu iyi bir fikir çünkü api istemcisini kodlarını değiştirmeden altta yatan modeli değiştirebiliyorsunuz.

ama

Bu harika ve bunu yapmak için iyi bir neden benim kod daha net hale getirecektir. YourClass.getCustomerConnection (cId), sizinCollection.get (id) .get (id) .getConnection() öğesinden daha nettir.Bu kodu kullanan kişilerin hayatını o kişi olsanız bile daha kolay hale getirmelisiniz.

(sonraki düzeyleri vardır Özellikle - AllConnections Haritası gibi aylara göre vb)

İyi o zaman öncesi planlama ve kod genişletilebilir yapıyoruz. Hangisi iyi OO uygulamasıdır. Benim düşünceme göre, kendi başınıza ulaştığınız tutku benim ne yapacağımı.

-1

Sen Map<K,V> uygulayan bir sınıf oluşturmak ve içten bir kap haritaya temsilci olabilir:

class CustomerConnections implements Map<String,LastConnection> { 
    private Map<String, LastConnection> customerConnections; 

    @Override 
    public LastConnection get(Object srvUrl) { 
     return customerConnections.get(srvUrl); 
    } 
    // all other needed operations; 
} 

bu yaklaşımla güzel bir şey de, bir standart olan bir Map dolaşması gerekiyor ise tanımlanmış sözleşme, ancak genellikle kaşlarını çattıran kütüphane sınıflarını genişletmekten kaçınırsınız.

DÜZENLEME: Aşağıda belirttiği gibi, bunun dışında hiçbir şey yapmak sınıfları oluşturma yatan koleksiyonu

+2

Masum küçük "hariç diğer tüm operasyonlar" yorumunun dışında, kazan delegasyonunun bir metrik yükü gizlenir. –

+0

@Michael Fair point – butterchicken

+1

Sadece bunu bir kez yapmanız gerekse de. Bir DelegatingMap uygular Harita tüm bu uzantıların temel sınıfı olarak kullanılabilir. – paulcm

0

Neden basit Map<String, LastConnection> bir anahtar olarak custId+"#"+srvurl kullanabilir?Veya Tuple veya Pair sınıfını iki kimlik içeren anahtar olarak kullanın ve hashCode() ve equals() - "en temiz" OO çözümü uygular.

+0

CustId + "#" + srvUrl anahtarını kullanmak, verilen tüm müşterilerinin bağlantılarını hızlı bir şekilde almama izin vermez. Ama yine de Tuple kütüphanesi fikri için teşekkürler, ona bakacağım. – Jakub

+0

Tamam, bu sizin orijinal probleminizin bir parçası değildi; Bu durumda Haritalar Haritası muhtemelen en iyi seçenek. –

1

Bu bilgileri depolamak için özel bir nesne oluşturmak istiyorum. Ne oluşturduğunuz basit bir koleksiyon yerine bir yöneticisi nesnesidir. Bu bilginin nasıl saklanacağının semantiği ileride değişebileceğinden, bir Haritadan veya diğer bilinen bir koleksiyon sınıfından türetilmemesini yapamazsınız.

yerine müşteri ve bağlantısını birbirine bağlayan bir sınıf uygulamak ve bu sınıf içinde

(arayüzünü ve kod kalanını etkilemeden daha sonra değiştirmek serbest olduğumuz bu) uygun toplama sınıfını kullanın

Müşteriniz/bağlantı yöneticisi sınıfınız basit bir kapsayıcıdan daha fazlasıdır. Meta verileri saklayabilir (ör. Bu ilişki ne zaman kurulduysa). Müşteri bilgisi verilen bağlantılarda arama yapabilir (eğer isterseniz). Altta yatan koleksiyon sınıfının bunları nasıl ele alacağı vb. Yerine, nasıl çoğaltabileceğinizi işleyebilir. Neler olup bittiğini kolayca anlamak için hata ayıklama/günlüğe kaydetme/performans izlemede kolayca yer açabilirsiniz.

0

Neden bu nesnelerin dışında nesneler arasındaki ilişkileri muhafaza edilmektedir. Daha sonra bu bilgileri kullanarak yöntemlere List<Customer> geçmesi

public class Customer 
{ 
    public List<LastConnection> getConnectionHistory() 
    { 
    ... 
    } 

    public List<LastConnection> getConnectionHistory(Service service) 
    { 
    ... 
    } 

    public List<LastConnection> getConnectionHistory(Date since) 
    { 
    ... 
    } 
} 

public class LastConnection 
{ 
    public Date getConnectionTime() 
    { 
    ... 
    } 

    public Service getService() 
    { 
    ... 
    } 
} 

:

aşağıdaki gibi bir şey öneririm.

+0

Ama sonra Müşteri sınıfımın LastConnection sınıfını bilmesi gerekiyor. Çeşitli paketler arasında tonlarca döngü yapabilirim. Elveda, yeniden kullanılabilirlik ... Kodumda dört paket olduğunu varsayalım: Çekirdek, LastConnections, AddressBook ve TransactionHistory (sadece örnekler). Eğer mypackage.core.Customer'ı LastConnections, AddressBook ve TransactionHistory hakkında bilgilendirirseniz (ve yönetirseniz) karışıklık yaratacaktır. LastConnection'ın Müşteri ve Adres Defterinin Müşteriyi bildiğini ve diğer yollarla ilgili olduğunu bilmesini tercih ederim. Bu şekilde bir sonraki işlevselliği uygulamak mevcut kodu değiştirmez. – Jakub

+0

Paketler ile kavanozlar demek istediğimi varsayıyorum. Bu durumda, bu ilişkileri kullanan uygulamada etki alanı sınıflarını (yukarıda açıklandığı gibi) tanımlayacağım. Etki alanı sınıfları paketlenmiş sınıfları uzmanlaştırabilir veya kapsülleyebilir, benim eğimim kapsüllemeye doğrudur çünkü paketlenmiş sınıfların uygulamanızla ilgili olmayan niteliklerini/özelliklerini gizleyebilirsiniz. Genellikle 'değer nesneleri' söz konusu olduğunda bunu kullandım, ancak uygulama, ayarlayıcıları 'değer nesneleri' açığa çıkarmamalıdır. Başka bir fayda ise paketlenmiş sınıflar değişirse uygulama izole edilir. –

1

ben de koleksiyonları kullanılacaktır verdiğini dikkate gerektiğini düşünüyorum ve veri nasıl elde edildiğini:

  • onlar basit Sonuç daha sonra, (örneğin) bir sayfada görüntülenecek setleri ise standart koleksiyonları kullanmak makul görünüyor - ve daha sonra bunları yönetmek için birçok standart kütüphane kullanabilirsiniz. Öte yandan
  • , bunlar değişken ve herhangi bir değişiklik (daha sonra veritabanlarına değişiklikleri yazabilirsiniz hangi vs.) tercih olabilir sonra kendi sınıfları içinde sarmalayarak, (örneğin) kalıcı için gerekiyorsa.

nasıl almak ve veri devam mı? Bir veritabanında saklanır, sonra belki, daha doğrusu veriyapılarıdır kendinizi (korumak ve sadece bağlantıların basit listeleri veya bağlantıların haritalar veya sayıları dönmek zorunda kalmak yerine müşteri ve/veya hizmet ve/veya aya göre LastConnections seçmek için SQL kullanabilirsiniz). Ya da her istek için bir sorgu çalıştırmak istemiyorsanız, tüm veri yapısını bellekte tutmaya ihtiyaç duyarsınız.

Kapsülleme genellikle iyi bir şeydir, özellikle de Law of Demeter'a bağlı kalmanıza yardımcı olabileceğinden - belki de koleksiyonlarda gerçekleştireceğiniz işlemlerin tümünü AllConnections sınıfına geri yükleyebilirsiniz (etkin bir DAO'dur)). Bu genellikle ünite testine yardımcı olabilir.

Ayrıca, HashMap'in neden kötülüğü dikkate aldığına göre, neden yalnızca önemsiz bir yardımcı yöntem eklemek istiyorsunuz? AllConnections kodunuzda, AllConnections her zaman bir HashMap ile aynı şekilde davranır - bu polimorfik olarak ikame edilebilir. Tabii ki, kendinizi HashMap kullanarak (TreeMap yerine) kilitliyorsunuz, fakat muhtemelen Harita ile aynı kamusal yöntemlere sahip olduğundan önemli değil. Ancak, gerçekten bunu yapmak isteyip istemediğiniz, gerçekten de koleksiyonları nasıl kullanacağınıza bağlı olarak değişir - Sadece uygulama kalıtımını otomatik olarak olarak hatalı olarak (genelde sadece!)