Hangi OO Tasarımı kullanılacak (bir Tasarım Deseni var mı?)


11

Bir 'Bar / Kulüp' (içtiğiniz / sosyalleştiğiniz bir yer) olan iki nesnem var.

Bir senaryoda bar adı, adres, mesafe, slogon'a ihtiyacım var

Başka bir senaryoda bar adı, adresi, web sitesi url'si, logoya ihtiyacım var

Aynı şeyi temsil eden ama farklı alanlara sahip iki nesnem var.

Değişmez nesneleri kullanmayı seviyorum, bu yüzden tüm alanlar yapıcıdan ayarlanır .

Bir seçenek iki kurucuya sahip olmak ve diğer alanları boş bırakmaktır:

class Bar {
     private final String name;
     private final Distance distance;
     private final Url url;

     public Bar(String name, Distance distance){
          this.name = name;
          this.distance = distance;
          this.url = null;
     }

     public Bar(String name, Url url){
          this.name = name;
          this.distance = null;
          this.url = url;
     }

     // getters
}

Alıcıları kullandığınızda null kontrol etmek zorunda kalacağınız için bunu sevmiyorum

Benim gerçek örneğimde, ilk senaryoda 3 alan ve ikinci senaryoda yaklaşık 10 tane var, bu yüzden iki kurucuya sahip gerçek bir acı olurdu, boş bırakacağım alanların miktarı ve daha sonra nesne kullanıldığında Hangisini Barkullandığınızı ve böylece hangi alanların boş ve hangilerinin kullanılamayacağını bilmiyorum.

Başka hangi seçeneklerim var?

İki sınıf denir BarPreviewve Bar?

Bir çeşit miras / arayüz?

Harika bir şey mi?


29
Vay be, aslında Barbir tanımlayıcı olarak meşru bir kullanım buldunuz !
Mason Wheeler

1
bazı özellikleri paylaşıyorsanız, seçeneklerden biri temel sınıfı uygulamak olacaktır.
Yusubov

1
Bunu hiç düşünmemiştim. Benim Bar / Foo köpek salonu için herhangi bir kod yazmak gerçekten kafa karıştırıcı olabilir.
Erik Reppen


4
@gnat İnsanlar nasıl tahmin ediyor? Bağlantı teklifinizden: You should only ask practical, answerable questions based on actual problems that you face.ve burada olan tam olarak budur
Blundell

Yanıtlar:


9

Düşüncelerim:

Alan adınızda temsil edildiği gibi bir "Çubuk", her iki yerde de gerekli olabilecek her şeye sahiptir: ad, adres, URL, logo, slogan ve "mesafe" (istekte bulunanın konumundan tahmin ediyorum). Bu nedenle, alan adınızda, verilerin daha sonra nerede kullanılacağı önemli değil, bir çubuk için yetkili veri kaynağı olan bir "Bar" sınıfı olmalıdır. Çubuğun verilerinde değişiklik yapılması ve gerektiğinde kaydedilebilmesi için bu sınıf değiştirilebilir olmalıdır.

Bununla birlikte, bu Bar nesnesinin verilerinin gerekli olduğu iki yeriniz vardır ve her ikisinin de yalnızca bir alt kümeye ihtiyacı vardır (ve bu verilerin değiştirilmesini istemezsiniz). Genel cevap bir "veri aktarım nesnesi" veya DTO'dur; değişmez özellik alıcılarını içeren bir POJO (düz ol 'Java nesnesi). Bu DTO'lar ana Bar etki alanı nesnesinde bir yöntem çağrılarak üretilebilir: "toScenario1DTO ()" ve "toScenario2DTO ()"; sonuçlar hidratlı bir DTO'dur (yani, uzun, karmaşık yapıcıyı tek bir yerde kullanmanız gerekir).

Verileri ana etki alanı sınıfına geri göndermeniz gerekiyorsa (güncellemek için; gerçek dünyanın geçerli durumunu yansıtacak şekilde değiştiremiyorsanız verilerin anlamı nedir?), DTO'lar veya yeni bir değiştirilebilir DTO kullanın ve bir "updateFromDto ()" yöntemi kullanarak Bar sınıfına geri verin.

EDIT: bir örnek sunmak için:

public class Bar {
     private String name;
     private Address address; 
     private Distance distance;
     private Url url;
     private Image logo;
     private string Slogan;

     public OnlineBarDto ToOnlineDto()
     {
         return new OnlineBarDto(name, address, url, logo);
     }

     public PhysicalBarDto ToPhysicalDto()
     {
         return new PhysicalBarDto(name, address, distance, slogan);
     }

     public void UpdateFromDto(PhysicalBarDto dto)
     {
         //validation logic here, or mixed into assignments

         name = dto.Name;
         address = dto.Address;
         distance = dto.Distance;
         slogan = dto.Slogan;
     }

     public void UpdateFromDto(OnlineBarDto dto)
     {
         //Validate DTO fields before performing assignments

         name = dto.Name;
         address = dto.Address;
         url= dto.Url;
         logo = dto.Logo;
     }

     // getters/setters - As necessary within the model and data access layers;
     // other classes can update the model using DTOs, forcing validation.
}

public class PhysicalBarDto
{
     public final String Name;
     public final Address Address;
     public final Distance Distance;
     public final String Slogan;

     public PhysicalBarDto(string Name, Address address, Distance distance, string slogan) 
     { //set instance fields using parameter fields; you know the drill }
}

public class OnlineBarDto
{
     public final String Name;
     public final Address Address;
     public final Image Logo;
     public final Url Url;

     public OnlineBarDto(string Name, Address address, Url url, Image logo) 
     { //ditto }
}

Adres, Mesafe ve Url sınıflarının kendileri değişmez olmalı veya DTO'larda kullanıldığında değişmez muadilleri ile değiştirilmelidir.


DTO kısaltması ne anlama geliyor? Söylediklerinizi tam olarak anlayamadım, lütfen çalışmış bir örneğe koyabilir misiniz? fyi Veriler bir sunucudan gelir, bu nedenle bu sınıfın her iki şekli de 'hidrate' olduğunda alanların değiştirilmesi gerekmez, sadece UI'de görüntülemek için kullanılır
Blundell

1
DTO, "veri aktarım nesnesi" anlamına gelir ve gerçek alan katmanını UI'ye (değişikliklere izin vermeden), verileri "zengin" alan katmanından kullanıcı arayüzü gibi üst katmanlara taşımak için kullanılan çok basit yapıya sahip bir veri sınıfına karşılık gelir DTO'nun değişmesi gerekmediği sürece kullanıcı arayüzünü etkilemeden alan adına yapılması).
KeithS

değişebilirliğin , modifikasyon veya kalıcılık üzerinde böyle bir etkisi yoktur.

4
@JarrodRoberson - dalga mı geçiyorsun? Bir sınıf değiştirilemezse (örneklemeden sonra yerinde değiştirilemez) veri katmanındaki verilerde değişiklik yapmanın tek yolu, farklı üyelerle aynı kaydı (aynı PK) temsil eden yeni bir örnek oluşturmaktır. Yeni bir örnek üreten "mutasyona uğrayan" yöntemler bunu kolaylaştırabilirken, yine de modifikasyon ve kalıcılık üzerinde büyük bir etkisi vardır .
KeithS

1
@JarrodRoberson Topluluğu dinle. Hatalısınız. . Aslında bu cevaptaki yorumların yarısı, yönetim kurulu çevresinde bazı temel OO eğitimine ihtiyacımız olduğunu gösteriyor - iğrenç ..
David Cowden

5

Özelliklerin yalnızca bir alt kümesini önemsiyorsanız ve bunların karıştırılmadığından emin olmak istiyorsanız, iki arabirim oluşturun ve bunu temel nesnenizle konuşmak için kullanın.


1
Bunu söylüyorsunuz, ancak Barsınıfı kullanarak bir örnek verebilir misiniz
Blundell

3

Oluşturucu Desen (kendisine ya da bir şey yakın) burada kullanım olabilir.

Değişmez nesnelere sahip olmak takdire şayan bir şeydir, ancak gerçek şu ki, Java'daki Yansıma ile hiçbir şey gerçekten güvenli değildir ;-).


Nasıl HawaiianPizzaBuilderçalıştığını görebiliyorum çünkü ihtiyacı olan değerler kodlanmış. Ancak, değerler bir kurucuya alınıp aktarılırsa bu kalıbı nasıl kullanabilirsiniz? HawaiianPizzaBuilderHala tüm alıcılar olurdu SpicyPizzaBuilderboş mümkündür böylece vardır. Bunu @Jarrods ile birleştirmediğiniz sürece Null Object Pattern. Bar
İle

+1 Bu gibi durumlarda yapıcı kullanıyorum, bir cazibe gibi çalışıyor - ancak bunu istediğimde null yerine makul varsayılanları ayarlamak dahil ancak bunlarla sınırlı değil
gnat

3

Buradaki kilit nokta, bir "Çubuk" un ne olduğu ve onu bir veya başka bağlamda nasıl kullandığınız arasındaki farktır .

Çubuk, gerçek dünyada (veya oyun gibi yapay bir dünyada) tek bir varlıktır ve bunu yalnızca BİR nesne örneği temsil etmelidir. Daha sonra, bu örneği bir kod segmentinden oluşturmazsanız, ancak bir yapılandırma dosyasından veya veritabanından yüklediğinizde, bu daha açık olur.

(Daha da ezoterik olmak için: her Bar örneğinin, programınız çalıştığında onu temsil eden nesneden farklı bir yaşam döngüsü vardır. Bu örneği oluşturan bir kaynak kodunuz olsa bile, Bar varlığı açıklandığı gibi " "kaynak kodunuzda hareketsiz durumda ve bu kod gerçekten bellekte oluşturduğunda" uyanır ...)

Uzun bir başlangıç ​​için özür dilerim, ama umarım bu benim açımdan netleşir. İhtiyacınız olan tüm özelliklere sahip ONE Bar sınıfınız ve her bir Bar varlığını temsil eden bir Bar örneğiniz var . Bu, kodunuzda doğrudur ve aynı örneği farklı bağlamlarda nasıl görmek istediğinizden bağımsızdır .

İkincisi , gerekli erişim yöntemlerini (getName (), getURL (), getDistance ()) içeren iki farklı arabirim ile temsil edilebilir ve Bar sınıfı her ikisini de uygulamalıdır. (Ve belki de "mesafe" "konum" olarak değişecektir ve getDistance () başka bir konumdan hesaplama olur :-))

Ancak yaratma, Bar varlığı içindir ve bu varlığı kullanmak istediğiniz şekilde değildir: bir kurucu, tüm alanlar.

DÜZENLENMİŞ: Kod yazabilirim! :-)

public interface Place {
  String getName();
  Address getAddress();
}

public interface WebPlace extends Place {
   URL getUrl();
   Image getLogo();
}

public interface PhysicalPlace extends Place {
  Double getDistance();
  Slogon getSlogon();
}

public class Bar implements WebPlace, PhysicalPlace {
  private final String name;
  private final Address address;
  private final URL url;
  private final Image logo;
  private final Double distance;
  private final Slogon slogon;

  public Bar(String name, Address address, URL url, Image logo, Double distance, Slogon slogon) {
    this.name = name;
    this.address = address;
    this.url = url;
    this.logo = logo;
    this.distance = distance;
    this.slogon = slogon;
  }

  public String getName() { return name; }
  public Address getAddress() { return address; }
  public Double getDistance() { return distance; }
  public Slogon getSlogon() { return slogon; }
  public URL getUrl() { return url; }
  public Image getLogo() { return logo; } 
}

1

Uygun Desen

Ne aradığınız en yaygın olarak Null Object Pattern. Adı beğenmezseniz Undefined Value Pattern, aynı semantik farklı etiket diyebilirsiniz . Bazen bu kalıba denir Poison Pill Pattern.

Bütün bu durumlarda, Nesne yerine geçer veya bir vekâlet Default Valueyerine null. It doesn't replace the semantic ofboş but makes it easier to work with the data model in a more predictable way becauseşimdi gerektiğini null` asla geçerli bir devlet olmasını.

Belirli bir sınıfın özel bir örneğini başka bir nullseçeneği olarak temsil etmek için ayırdığınız bir Örüntüdür Default Value. Bu şekilde kontrol etmeniz gerekmez null, kimliği bilinen NullObjectörneğinize göre kontrol edebilirsiniz . Endişelenmeden yöntemleri ve benzerlerini güvenle çağırabilirsiniz NullPointerExceptions.

Bu şekilde nullödevlerinizi temsili NullObjectörnekleriyle değiştirirsiniz ve işlem tamamlanır.

Uygun Nesneye Dayalı Analiz

Bu şekilde Interface, polimorfizm için ortak bir noktaya sahip olabilir ve yine de arayüzün belirli uygulamalarında veri yokluğu konusunda endişelenmeye karşı korunabilirsiniz. Bu nedenle, bazılarının Barweb varlığı olmayabilir ve bazılarının inşaat sırasında konum verileri olmayabilir. aynı şeyi söyleyen veriler Null Object Patteriçin her biri için varsayılan bir değer vermenizi sağlar marker, burada hiçbir şey sağlanmamıştır, NullPointerExceptionher yerde kontrol ile anlaşma olmadan .

Uygun Nesneye Dayalı Tasarım

İlk bir var abstracthem tüm niteliklerin bir süper dizi olan uygulanmasını Barve Clubhisse.

class abstract Establishment 
{
     private final String name;
     private final Distance distance;
     private final Url url;

     public Bar(final String name, final Distance distance, final Url url)
     {
          this.name = name;
          this.distance = distance;
          this.url = url;
     }

     public Bar(final String name, final Distance distance)
     {
          this(name, distance, Url.UNKOWN_VALUE);
     }

     public Bar(final String name, final Url url)
     {
          this(name, Distance.UNKNOWN_VALUE, url);
     }

     // other code
}

Sonra bu alt sınıflarını uygulayabilir Establishmentsınıfının ve her biri için ihtiyaç yalnızca belirli şeyler eklemek Barve Clubdiğer için geçerli değildir sınıflar.

süreklilik

Bu yer tutucu nesneler, doğru şekilde yapılandırıldıklarında, özel bir işlem yapmadan da bir veritabanında şeffaf bir şekilde saklanabilir.

Gelecek Kanıtı

Daha sonra bir Kontrol / Bağımlılık Enjeksiyonu bandwagonuna atlamaya karar verdiyseniz, bu model bu marker nesnelerini de enjekte etmeyi kolaylaştırır.


0

Bence sorun şu senaryolardan birinde bir Bar modellememenizdir (Ve iki farklı problem, nesne, vb. Modelliyorsunuz). Bir sınıf Bar görürsem, içecekler, menüler, mevcut koltuklar ve nesnenizle ilgili bazı işlevler beklerdim. Nesnelerinizin davranışlarını görürsem, bir kuruluş hakkındaki bilgileri modellersiniz. Bar, şu anda bunları kullandığınız şeydir, ancak gerçekte uyguladıkları davranış değildir. (Başka bir bağlamda: Bir Evliliği modelliyorsanız, iki eş değişkeniniz olacaktır: Kişi eşi; Kişi kocası; bir eş, o anda o nesneye verdiğiniz geçerli roldür, ancak nesne hala bir Kişidir). Böyle bir şey yapardım:

class EstablishmentInformation {
     private final String name;

     public EstablishmentInformation(String name){
          this.name = name;
     }

     // getters
}

class EstablishmentLocationInformation {
    EstablishmentInformation establishmentInformation;
     private final Distance distance;

     public EstablishmentLocationInformation (String name, Distance distance){
          this.establishmentInformation = new EstablishmentInformation(name)
          this.distance = distance;
     }
}

class EstablishmentWebSiteInformation {
    EstablishmentInformation establishmentInformation;
     private final Url url;

     public EstablishmentWebSiteInformation(String name, Url url){
          this.establishmentInformation = new EstablishmentInformation(name)
          this.url = url;
     }
}

-1

Bunu aşırı karmaşıklaştırmaya gerçekten gerek yok. İki farklı nesneye mi ihtiyacınız var? İki sınıf yapın.

class OnlineBar {
     private final String name;
     private final Url url;
     public OnlineBar(String name, Url url){
          this.name = name;
          this.url = url;
     }

     // ...
}
class PhysicalBar {
     private final String name;
     private final Distance distance;
     public PhysicalBar(String name, Distance distance){
          this.name = name;
          this.distance = distance;
     }
     //...
}

Bunları eşit olarak çalıştırmanız gerekiyorsa, bir arayüz eklemeyi veya yansıma kullanmayı düşünün.


@David: Ah hayır. Ortak bir veri üyesi gibi. ACİL DURUM!
DeadMG

ortak bir arayüz olmadan bu çözümde polimorfizm yoktur , bu sınıfların hiçbiri bu zayıf tasarım kararıyla diğerinin yerine kullanılamaz. Özelliklerin aynı üst kümesine sahip olmadıkları değil, varsayılan olarak bu özelliklerin bazılarıdır null. Unutmayın , verinin yokluğunull anlamına gelmez, verinin yokluğu anlamına gelir .

1
@DeadMG Bu, büyük olasılıkla, paylaşılan değerleri tam olarak üst nesnelere gruplama fikrinde bir alıştırmadır.
David Cowden

OP ikame edilebilirlik için herhangi bir ihtiyaç belirtmez . Dediğim gibi, sadece bir arayüz ekleyebilir veya isterseniz yansımayı kullanabilirsiniz.
DeadMG

@DeadMG Ancak yansıma, tek bir ortam kullanarak çapraz platform mobil uygulaması yazmaya çalışmak gibidir - Çalışabilir, ancak doğru değildir . Yansıtma kullanarak bir yöntemi çağırmanın cezası, normal yöntem çağrısından 2 ila 50 kat daha yavaştır. Yansıma bir tedavi değil ..
David Cowden

-1

Bu tür bir sorunu olan herkese cevabım, yönetilebilir adımlara ayırmaktır .

  1. İlk önce iki sınıf oluşturun BarOneve BarTwo(veya her ikisini de Barfarklı paketlerde çağırın )
  2. Nesnelerinizi ayrı sınıflar olarak kullanmaya başlayın, şimdilik kod çoğaltma konusunda endişelenmeyin. Birinden diğerine geçtiğinizde (yinelenen yöntemler) fark edeceksiniz
  3. Hiçbir şekilde ilişkili olmadıklarını öğrenebilirsiniz ve bu yüzden kendinize sormanız gerekir , ikisi de gerçektenbar rahatsız edici sınıfı şimdi temsil ettiği şekilde yeniden adlandırmazsa
  4. Ortak alanlar veya davranışlar bulursanız , ortak bir davranışla interfaceveya superclassortak davranışla
  5. Bir edindikten sonra interfaceveya superclassbir yaratabilecek builderveya factorysizin uygulama nesneleri almak / oluşturmak için

(4 ve 5 bu soruya verilen diğer cevapların ne olduğudur)


-2

Bir temel sınıfa ihtiyacınız var, örneğin, adı ve adresi olan bir Konum . Şimdi, iki sınıf Bar ve BarPreview temel sınıf Konum genişletmek var . Her sınıfta süper sınıf ortak değişkenlerini ve ardından benzersiz değişkenlerinizi başlatırsınız:

public class Location {
    protected final String name;
    protected final String address:

    public Location (String locName, String locAddress) {
    name = locName;
    address = locAddress
    }

}

public class Bar extends Location {
    private final int dist;
    private final String slogan;

    public Bar(String barName, String barAddress,
               int distance, String barSlogan) {
    super(locName, locAddress);
    dist = distance;
    slogan = barSlogan;
    }
}

BarPreview sınıfı için de benzer ..

Eğer geceleri daha iyi uyku yaparsa, tüm örneklerini değiştirmek Konum ile benim kodunda AnythingYouThinkWouldBeAnAppropriateNameForAThingThatABarExtendsSuchAsFoodServicePlace - ffs.


OP bu örneğin değişmez olmasını ister, yani her şeyin olması gerekir final.

1
Bu işe yarayabilir, final@David alanlarına ihtiyacınız var. bu mantıklı Bargelmemelidir extend Location. Belki de Bar extends BaseBarve BarPreview extends BaseBarbu isimler gerçekten de kulağa pek hoş gelmiyor, daha zarif bir şey umuyordum
Blundell

@JarrodRoberson Ben sadece onun için çizim yapıyorum .. sadece değişmez olması için final ekleyin. Bu bir beyinsizdir. OP'nin sorusundaki temel sorun, bir temel sınıfı ve bir temel sınıfı genişleten iki ayrı sınıfı nasıl bilmediğidir. Ben sadece bunu detaylandırıyorum.
David Cowden

@Blundell dünyada ne hakkında konuşuyorsun? Bir Bar olan bir yer . Konumumu BaseBar'ınızla değiştirin ve tam olarak aynı şeydir .. Bir sınıf diğerini genişlettiğinde sınıfın genişlettiği Base [ClassThatWillExtend] olarak adlandırılması gerekmediğini biliyor musunuz?
David Cowden

1
@DavidCowden, cevabınızı diğer tüm cevapların altında yorum olarak tanıtmayı durdurabilir misiniz, lütfen?
marktani
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.