Koleksiyonlara öğe eklemenin daha “doğal” bir yolu var mı? [kapalı]


13

Bir koleksiyona bir şey eklemenin en yaygın yolunun, Addbir koleksiyonun sağladığı bir tür yöntemi kullanmak olduğunu düşünüyorum:

class Item {}    
var items = new List<Item>();
items.Add(new Item());

ve aslında bu konuda alışılmadık bir şey yok.

Ancak merak ediyorum neden böyle yapmıyoruz:

var item = new Item();
item.AddTo(items);

ilk yönteme göre bir şekilde daha doğal görünüyor . Bu, Itemsınıfın aşağıdaki gibi bir özelliği olduğunda Parent:

class Item 
{
    public object Parent { get; private set; }
} 

ayarlayıcıyı özel yapabilirsiniz. Bu durumda elbette bir genişletme yöntemi kullanamazsınız.

Ama belki yanılıyorum ve bu modeli daha önce hiç görmedim çünkü bu çok nadir? Böyle bir kalıp olup olmadığını biliyor musunuz?

Gelen C#bir uzantı yöntem için yararlı olacaktır

public static T AddTo(this T item, IList<T> list)
{
    list.Add(item);
    return item;
}

Diğer dillere ne dersiniz? Sanırım birçoğunda Itemsınıf bir ICollectionItemarabirim diyelim .


Güncelleme-1

Biraz daha düşünüyorum ve birden fazla koleksiyona bir öğe eklemek istemiyorsanız bu desen gerçekten yararlı olacaktır.

test ICollectablearayüzü:

interface ICollectable<T>
{
    // Gets a value indicating whether the item can be in multiple collections.
    bool CanBeInMultipleCollections { get; }

    // Gets a list of item's owners.
    List<ICollection<T>> Owners { get; }

    // Adds the item to a collection.
    ICollectable<T> AddTo(ICollection<T> collection);

    // Removes the item from a collection.
    ICollectable<T> RemoveFrom(ICollection<T> collection);

    // Checks if the item is in a collection.
    bool IsIn(ICollection<T> collection);
}

ve örnek bir uygulama:

class NodeList : List<NodeList>, ICollectable<NodeList>
{
    #region ICollectable implementation.

    List<ICollection<NodeList>> owners = new List<ICollection<NodeList>>();

    public bool CanBeInMultipleCollections
    {
        get { return false; }
    }

    public ICollectable<NodeList> AddTo(ICollection<NodeList> collection)
    {
        if (IsIn(collection))
        {
            throw new InvalidOperationException("Item already added.");
        }

        if (!CanBeInMultipleCollections)
        {
            bool isInAnotherCollection = owners.Count > 0;
            if (isInAnotherCollection)
            {
                throw new InvalidOperationException("Item is already in another collection.");
            }
        }
        collection.Add(this);
        owners.Add(collection);
        return this;
    }

    public ICollectable<NodeList> RemoveFrom(ICollection<NodeList> collection)
    {
        owners.Remove(collection);
        collection.Remove(this);
        return this;
    }

    public List<ICollection<NodeList>> Owners
    {
        get { return owners; }
    }

    public bool IsIn(ICollection<NodeList> collection)
    {
        return collection.Contains(this);
    }

    #endregion
}

kullanımı:

var rootNodeList1 = new NodeList();
var rootNodeList2 = new NodeList();

var subNodeList4 = new NodeList().AddTo(rootNodeList1);

// Let's move it to the other root node:
subNodeList4.RemoveFrom(rootNodeList1).AddTo(rootNodeList2);

// Let's try to add it to the first root node again... 
// and it will throw an exception because it can be in only one collection at the same time.
subNodeList4.AddTo(rootNodeList1);

13
item.AddTo(items)uzantı yöntemleri olmadan bir diliniz olduğunu varsayalım: doğal ya da değil, addTo'u desteklemek için her türün bu yönteme ihtiyacı vardır ve uygulamayı destekleyen her tür koleksiyon için bunu sağlar. Bu, şimdiye kadar duyduğum her şey arasında bağımlılık getirmenin en iyi örneği gibi : P - Bence buradaki yanlış öneri, 'gerçek' hayata bazı programlama soyutlamalarını modellemeye çalışıyor. Bu genellikle yanlış gidiyor.
stijn

1
@ t3chb0t Hehe, iyi bir nokta. İlk konuşma dilinizin, yapının daha doğal göründüğü şeyi etkileyip etkilemediğini merak ediyordum. Bana göre, doğal görünen tek şey olurdu add(item, collection), ama bu iyi bir OO stili değil.
Kilian Foth

3
@ t3chb0t Konuşulan dilde, işleri normal veya refleks olarak okuyabilirsiniz. "John, lütfen bu elmayı taşıdığın şeye ekle." vs "Elma taşıdığın şeye eklenmeli, John." OOP dünyasında, refleksif pek mantıklı değil. Bir şeyin genel olarak konuşan başka bir nesneden etkilenmesini istemezsiniz. OOP'ta buna en yakın ziyaretçi kalıbıdır . Bunun norm olmamasının bir nedeni var.
Neil

3
Bu kötü bir fikir etrafında güzel bir yay koyuyor .
Telastyn

3
@ t3chb0t İsim Krallığı'nın ilginç bir okuma yaptığını görebilirsiniz.
paul

Yanıtlar:


51

Hayır, item.AddTo(items)daha doğal değil. Bunu aşağıdakilerle karıştırdığınızı düşünüyorum:

t3chb0t.Add(item).To(items)

Bunun doğru olan items.Add(item)çok doğal ingilizce diline yakın değildir. Ama item.AddTo(items)doğal ingilizce dilinde de duymuyorsunuz , değil mi? Normalde öğeyi listeye eklemesi gereken biri vardır. Bir süpermarkette çalışırken veya yemek pişirirken ve eklerken olun.

Programlama dilleri durumunda, bir liste hem yapar, böylece onu yaptı: onun öğeleri saklamak ve kendisi ekleyerek sorumlu olmak.

Yaklaşımınızdaki sorun, bir öğenin bir listenin varlığından haberdar olması gerektiğidir. Ama hiç liste olmasa bile bir eşya var olabilir, değil mi? Bu listelerin farkında olmaması gerektiğini gösterir. Listeler hiçbir şekilde kodunda yer almamalıdır.

Ancak listeler öğeler olmadan mevcut değildir (en azından işe yaramazlar). Bu nedenle, eşyalarını biliyorlarsa iyi olur - en azından genel bir biçimde.


4

@valenterry endişelerin ayrılması konusunda oldukça haklı. Sınıflar, onları içerebilecek çeşitli koleksiyonlar hakkında hiçbir şey bilmemelidir. Her yeni koleksiyon türü oluşturduğunuzda , öğe sınıfını değiştirmek istemezsiniz .

Bahsedilen...

1. Java değil

Java burada size yardımcı olmaz, ancak bazı diller daha esnek, genişletilebilir sözdizimine sahiptir.

Scala'da items.add (item) şöyle görünür:

  item :: items

Where :: , listelere öğe ekleyen bir operatördür. Ne istiyorsan öyle görünüyor. Aslında sözdizimsel şeker

  items.::(item)

where :: , Java'nın list.add komutuna etkili bir şekilde eşdeğer bir Scala liste yöntemidir . Öyleyse, ilk versiyonda öğe kendini öğelere ekliyormuş gibi görünse de, gerçekte öğeler ekleme yapıyor. Her iki versiyon da geçerlidir Scala

Bu hile iki adımda yapılır:

  1. Scala'da operatörler gerçekten sadece yöntemlerdir. Eğer Scala haline Java listelerini içe, sonra (item) resi de yazılabilir öğeler öğe eklemek . Scala'da 1 + 2 aslında 1 + (2) . Noktalar ve köşeli ayraçlar yerine boşlukları olan sürümde, Scala ilk nesneyi görür, bir sonraki sözcükle eşleşen bir yöntem arar ve (yöntem parametre alıyorsa) bir sonraki şeyi (veya şeyleri) bu yönteme iletir.
  2. Scala'da, iki nokta üst üste ile biten operatörler soldan ziyade sağ çağrışımsaldır. Scala yöntemi gördüğünde, sağdaki yöntem sahibini arar ve soldaki değeri besler.

Çok sayıda operatörden memnun değilseniz ve addTo'nuzu istiyorsanız , Scala başka bir seçenek sunar: örtülü dönüşümler. Bu iki adım gerektirir

  1. Örneğin, ListItem adlı yapıcısında bir öğe alan ve bu öğeyi belirli bir listeye ekleyebilen bir addTo yöntemine sahip bir sarmalayıcı sınıfı oluşturun .
  2. Bir öğeyi parametre olarak alan ve bu öğeyi içeren bir ListItem döndüren bir işlev oluşturun . Olarak işaretlemek örtük .

Eğer örtülü dönüşüm etkin ve Scala görür edilir

  item addTo items

veya

  item.addTo(items)

ve öğe addTo içermiyorsa , örtülü dönüştürme işlevleri için geçerli kapsamda arama yapar. Dönüşüm işlevlerinin herhangi bir türünü döndürür yapar bir sahip addto yöntemi, bu gerçekleşir:

  ListItem.(item).addTo(items)

Şimdi, örtülü dönüşümleri zevkinize daha fazla yönlendirebilirsiniz, ancak tehlike ekler, çünkü belirli bir bağlamdaki tüm örtülü dönüşümlerin açıkça farkında değilseniz kod beklenmedik şeyler yapabilir. Scala'da bu özellik artık varsayılan olarak etkinleştirilmemesinin nedeni budur. (Ancak, kendi kodunuzda etkinleştirilip etkinleştirilmeyeceğini seçebilseniz de, diğer insanların kütüphanelerindeki durumu hakkında hiçbir şey yapamazsınız. İşte ejderhalar).

İki nokta üst üste sonlandırılan işleçler çirkindir, ancak tehdit oluşturmaz.

Her iki yaklaşım da endişelerin ayrılması ilkesini korur. Öğe koleksiyon hakkında bilgi sahibi gibi görünmesini sağlayabilirsiniz , ancak iş aslında başka bir yerde yapılır. Size bu özelliği vermek isteyen diğer diller de en az dikkatli olmalı.

2. Java

Sözdiziminin sizin tarafınızdan genişletilemediği Java'da, yapabileceğiniz en iyi şey, listeleri bilen bir tür sarıcı / dekoratör sınıfı oluşturmaktır. Öğelerinizin listelere yerleştirildiği alan adında, bunları bu kutuya sarabilirsiniz. Bu alan adından ayrıldıklarında onları dışarı çıkarın. Belki ziyaretçi kalıbı en yakındır.

3. tl; dr

Scala, diğer bazı diller gibi daha doğal sözdiziminde size yardımcı olabilir. Java size sadece çirkin, barok desenler sunabilir.


2

Uzantı yönteminizle hala kodunuza faydalı bir şey eklememesi için Add () öğesini çağırmanız gerekir. Addto yönteminiz başka bir işlem yapmadıysa, bunun var olması için bir neden yoktur. İşlevsel bir amacı olmayan kod yazmak, okunabilirlik ve sürdürülebilirlik açısından kötüdür.

Eğer jenerik olmayan bırakırsanız sorunlar daha da belirginleşir. Artık içinde bulunabileceği farklı koleksiyon türleri hakkında bilmeniz gereken bir öğeniz var. Bu, tek sorumluluk ilkesini ihlal ediyor. Bir öğe yalnızca verileri için bir tutucu değil, aynı zamanda koleksiyonları da manipüle eder. Bu nedenle, her ikisini de tüketen kod parçasına kadar koleksiyonlara öğe ekleme sorumluluğunu bırakıyoruz.


Bir çerçeve farklı türde nesne referanslarını tanıyabiliyorsa (örneğin, mülkiyeti kapsülleyenler ile sahip olmayanlar arasında ayrım yapmak), mülkiyeti kapsülleyen bir referansın, alabilir. Her şey bir sahiple sınırlıysa , thing.giveTo(collection)[[ collectionbir “acceptOwnership” arabirimi] aracılığıyla sahipliğin aktarılması, sahipliğe collectionel konan bir yöntem eklemekten daha mantıklı olacaktır . Tabii ki ...
Supercat

... Java'daki çoğu şeyin bir sahiplik kavramı yoktur ve nesne referansı, tanımlanan nesne dahil edilmeden bir koleksiyonda saklanabilir.
Supercat

1

Bence "ekle" kelimesine takıntılısın. Bunun yerine, desende değişiklik olmadan size daha İngilizce dostu bir sözdizimi sağlayacak "toplamak" gibi alternatif bir kelime aramayı deneyin:

items.collect(item);

Yukarıdaki yöntem, items.add(item);tek farkın farklı kelime seçimi olmasıyla tamamen aynıdır ve İngilizce'de çok güzel ve "doğal olarak" akar.

Bazen sadece değişkenlerin, yöntemlerin, sınıfların vb. Yeniden adlandırılması, modelin vergilendirilmesinin yeniden tasarlanmasını önler.


collectbazen bir öğe koleksiyonunu bir tür tekil sonuca indiren yöntemlerin adı olarak kullanılır (bu aynı zamanda bir koleksiyon da olabilir). Bu sözleşme, bu bağlamda adın kullanımını son derece popüler hale getiren Java Steam API tarafından kabul edildi . Cevabınızda bahsettiğiniz bağlamda kullanılan kelimeyi hiç görmedim. I 's ile sopa tercih ediyorum addveyaput
toniedzwiedz

@toniedzwiedz, Yani düşün pushya da enqueue. Mesele belirli bir örnek adı değil, farklı bir adın OP'nin İngilizce esque dilbilgisi arzusuna daha yakın olabileceği gerçeği.
Brian S

1

Genel durum için böyle bir örüntü olmamasına rağmen (başkaları tarafından açıklandığı gibi), benzer bir örüntünün kendi değerlerine sahip olduğu bağlam, ORM'de çift yönlü bir-çok ilişkisidir.

Bu bağlamda iki varlığınız olur diyelim Parentve Child. Bir ebeveynin çok sayıda çocuğu olabilir, ancak her çocuk bir ebeveyne aittir. Uygulama ilişkilendirmenin her iki uçtan da (çift yönlü) gezinebilir olmasını gerektiriyorsa, List<Child> childrenüst sınıfta ve Parent parentalt sınıfta a olur.

Dernek her zaman karşılıklı olmalıdır. ORM çözümleri genellikle yükledikleri varlıklar için bunu uygular. Ancak uygulama bir varlığı (kalıcı veya yeni) manipüle ederken aynı kuralları uygulamalıdır. Benim tecrübelerime göre, genellikle kamusal kullanım için olmayan bir Parent.addChild(child)yöntem bulursunuz Child.setParent(parent). İkincisinde, örneğinizde uyguladığınız denetimlerin aynısını bulabilirsiniz. Ancak diğer rota da uygulanabilir: Child.addTo(parent)hangi çağrılar Parent.addChild(child). Hangi yolun en iyi olduğu durumdan duruma değişir.


1

Java'da tipik bir toplama tutmaz şeyler tutan --O referansları şeyler, daha doğrusu kopya ait referanslar şeyler. Buna karşılık, nokta-üye sözdizimi, genellikle referansın kendisinden ziyade bir referansla tanımlanan şey üzerinde hareket etmek için kullanılır.

Bir kaseye bir elma yerleştirmek, elmanın bazı yönlerini değiştirir (örneğin konumu), bir kaseye "nesne # 29521" yazan bir kağıt parçası yerleştirmek, elmayı bile olsa hiçbir şekilde değiştirmez. nesne # 29521. Ayrıca, koleksiyonlar referansların kopyalarını tuttuğundan, bir kaseye "nesne # 29521" diyen bir kağıt parçası yerleştirmek için Java eşdeğeri yoktur. Bunun yerine, # 29521 numarasını yeni bir kağıda kopyalar ve bunu kaseye verin (vermeyin), orijinali etkilenmeden bırakırlar.

Aslında, Java'daki nesne referansları ("kağıt kaymaları") pasif olarak gözlemlenebildiğinden, ne referanslar ne de bu şekilde tanımlanan nesnelerin kendileriyle ne yapıldığını bilmenin herhangi bir yolu yoktur. Koleksiyonun varlığından hiçbir şey bilmeyebilecek nesnelere bir dizi referans tutmak yerine, içinde kapsüllenen nesnelerle çift yönlü bir ilişki kuran bazı koleksiyon türleri vardır. Bu tür bazı koleksiyonlarda, bir koleksiyona kendisini eklemek için bir yöntem istemek mantıklı olabilir (özellikle bir nesne aynı anda sadece bir koleksiyonda bulunabiliyorsa). Bununla birlikte, genel olarak, çoğu koleksiyon bu kalıba uymaz ve bu referanslar tarafından tanımlanan nesneler üzerinde herhangi bir katılım olmaksızın nesnelere yapılan referansları kabul edebilir.


bu yazıyı okumak oldukça zor (metnin duvarı). Sakıncası var düzenleyebilir daha iyi bir şekle ing?
gnat

@gnat: Daha iyi mi?
Supercat

1
@gnat: Üzgünüz - bir ağ hıçkırışı düzenlemeyi gönderme girişimimin başarısız olmasına neden oldu. Şimdi daha iyi misin?
supercat

-2

item.AddTo(items)pasif olduğu için kullanılmaz. Cf "Öğe listeye eklenir" ve "oda dekoratör tarafından boyanır".

Pasif konstrüksiyonlar iyidir, ama en azından İngilizce olarak aktif konstrüksiyonları tercih etme eğilimindeyiz.

OO programlamasında, pardigm, oyunculuk yapanlara değil, aktörlere önem verir, bu yüzden sahip olduk items.Add(item). Liste bir şey yapan şeydir. Bu aktör, bu yüzden bu daha doğal bir yol.


Eğer aynı şey söylenebilir - izafiyet teorisini - O nereye ;-) duruyor bağlıdır list.Add(item) bir öğeye listesine ekleniyor veya bir liste bir öğe ekler veya yaklaşık item.AddTo(list) öğe listesine kendisini ekler veya ben öğe eklemek listeye veya öğenin listeye eklendiğini söylediğiniz gibi - hepsi doğru. Soru şu: Aktör kim ve neden? Bir öğe aynı zamanda bir aktör ve grubun bu öğeye sahip olmak istemediği bir gruba (liste) katılmak istiyor ;-) Öğe aktif olarak bir grupta olmak için hareket ediyor.
t3chb0t

Aktör, aktif şeyi yapan şeydir. “Wanting” pasif bir şeydir. Programlamada, bir öğe eklendiğinde değişen listedir, öğe hiç değişmemelidir.
Matt Ellen

... bir Parentmülkiyeti olduğu zaman hoşuna gidiyor . Kuşkusuz İngilizce olmayabilir anadil ama bildiğim kadarıyla biliyorum olarak isteyen sürece pasif değil istediğim ediyorum ya da bir şeyler yapmak istiyor ; -]
t3chb0t

Bir şey istediğinizde hangi işlemi yapıyorsunuz? Demek istediğim bu. Mutlaka dilbilgisel olarak pasif değil, anlamsal olarak öyle. WRT ebeveyn özellikleri, bunun bir istisna olduğundan emin olun, ancak tüm sezgisel taramaların bunlara sahip olması.
Matt Ellen

Ancak List<T>(en azından içinde bulunan System.Collections.Generic) hiçbir Parentözelliği güncellemez . Eğer jenerik olması gerekiyorsa nasıl olabilir? İçeriğini eklemek genellikle kabın sorumluluğundadır.
Arturo Torres Sánchez
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.