Değişmez sınıflar hangi noktada yük haline gelir?


16

Veri modelinizi tutmak için sınıflar tasarlarken, değiştirilemeyen nesneler oluşturmak yararlı olabilir, ancak yapıcı parametre listelerinin ve derin kopyaların yükü çok fazla olur ve değişmez kısıtlamayı terk etmek zorunda kalırsınız?

Örneğin, burada adlandırılmış bir şeyi temsil eden değişmez bir sınıf var (C # sözdizimini kullanıyorum ancak ilke tüm OO dilleri için geçerlidir)

class NamedThing
{
    private string _name;    
    public NamedThing(string name)
    {
        _name = name;
    }    
    public NamedThing(NamedThing other)
    {
         this._name = other._name;
    }
    public string Name
    {
        get { return _name; }
    }
}

Adlandırılmış şeyler oluşturulabilir, sorgulanabilir ve yeni adlandırılmış şeylere kopyalanabilir, ancak ad değiştirilemez.

Bu iyi ama başka bir özellik eklemek istediğimde ne olur? Kurucuya bir parametre eklemek ve kopya yapıcısını güncellemek zorunda; bu çok fazla iş değil ama problemler görebildiğim kadarıyla karmaşık bir nesneyi değişmez kılmak istediğimde başlıyor .

Sınıf, diğer karmaşık sınıfları içeren may öznitelikleri ve koleksiyonlar içeriyorsa, bana göre yapıcı parametre listesi kabus olur.

Peki bir sınıf hangi noktada değişmez olamayacak kadar karmaşık hale geliyor ?


Modelimdeki sınıfları değişmez kılmak için her zaman çaba gösteririm. Büyük, uzun yapıcı parametre listeleriniz varsa, belki sınıfınız çok büyük ve bölünebilir mi? Alt düzey nesneleriniz de değişmezse ve aynı modeli izlerse, üst düzey nesneleriniz acı çekmemelidir (çok fazla). Mevcut bir sınıfı değiştirilemez hale getirmek için sıfırdan başladığımda bir veri modelini değişmez hale getirmekten çok daha zor buluyorum.
Kimse

1
Bu soruda önerilen Oluşturucu modeline bakabilirsiniz: stackoverflow.com/questions/1304154/…
Ant

MemberwiseClone'a baktınız mı? Her yeni üye için kopya oluşturucuyu güncellemeniz gerekmez.
kevin cline

3
@Tony Koleksiyonlarınız ve içerdikleri her şey değişmezse, derin bir kopyaya ihtiyacınız yoktur, sığ bir kopya yeterlidir.
mjcopple

2
Bir yana, sınıfta "oldukça" değişmez, ancak tamamen değil olması gereken sınıflarda "set-once" alanları kullanıyorum. Bunun büyük inşaatçılar sorununu çözdüğünü, ancak değişmez sınıfların faydalarının çoğunu sağladığını düşünüyorum. (yani, iç sınıf kodunuz bir değer değişikliği konusunda endişelenmenize gerek yoktur)
Earlz

Yanıtlar:


23

Yük olduklarında? Çok hızlı bir şekilde (özellikle tercih ettiğiniz dil değişmezlik için yeterli sözdizimsel destek sağlamıyorsa.)

Değişmezlik çok çekirdekli ikilem için gümüş mermi olarak satılıyor. Ancak çoğu OO dilinde değişmezlik, sizi modelinize ve işleminize yapay eserler ve uygulamalar eklemeye zorlar. Her karmaşık değişmez sınıf için eşit derecede karmaşık (en azından dahili olarak) bir yapıcıya sahip olmalısınız. Nasıl tasarlarsanız tasarlayın, fotoğraflar güçlü bir bağlantı sağlar (bu yüzden onları tanıtmak için iyi bir nedenimiz daha iyi olur.)

Her şeyi karmaşık olmayan küçük sınıflarda modellemek her zaman mümkün değildir. Bu nedenle, büyük sınıflar ve yapılar için onları yapay olarak bölümlere ayırıyoruz - bu , etki alanı modelimizde mantıklı olduğu için değil, karmaşık örnekleme ve kod oluşturucularıyla uğraşmak zorunda olduğumuz için.

İnsanlar, Java veya C # gibi genel amaçlı bir dilde değişmezlik fikrini çok ileri götürdüğünde, her şeyi değişmez kılar. Sonuç olarak, insanların bu tür şeyleri kolaylıkla desteklemeyen dillerde s-ifade yapılarını zorladığını görürsünüz.

Mühendislik, tavizler ve ödünleşmeler yoluyla modelleme eylemidir. Birisi her şeyin kurgu ile değiştirilemez hale getirilmesi, çünkü birisi her şeyin X veya Y işlevsel dilinde (tamamen farklı bir programlama modeli) değişmez olduğunu okuduğunu kabul edilebilir değil. Bu iyi bir mühendislik değil.

Küçük, muhtemelen üniter şeyler değişmez hale getirilebilir. Daha karmaşık şeyler mantıklı olduğunda değişmez hale getirilebilir . Ancak değişmezlik gümüş bir kurşun değildir. Hataları azaltma, ölçeklenebilirliği ve performansı artırma yeteneği, bunlar değişmezliğin tek işlevi değildir. Uygun mühendislik uygulamalarının bir fonksiyonudur . Sonuçta, insanlar değişmezlik olmadan iyi, ölçeklenebilir bir yazılım yazdılar.

Bir etki alanı modeli bağlamında mantıklı olanın dışında yapıldığında, sebepsiz yapılırsa, değişmezlik gerçekten hızlı bir yük haline gelir (kazara karmaşıklığa katkıda bulunur).

Birincisi, bundan kaçınmaya çalışıyorum (iyi bir sözdizimsel desteği olan bir programlama dilinde çalışmazsam.)


6
luis, basit ama sağlam mühendislik prensiplerinin açıklanmasıyla yazılan iyi yazılmış, pragmatik olarak doğru cevapların, en son teknoloji kodlama soluklarını kullananların ne kadar oy alma eğiliminde olduklarını fark ettiniz mi? Bu harika, harika bir cevap.
Huperniketes

3
Teşekkürler :) Rep eğilimleri kendim fark ettim, ama sorun değil. Fad fanboys daha sonra daha iyi saatlik ücretler, hahah :) jk tamir olsun kod
karmaşası

4
Gümüş kurşun? Hayır. C # / Java gariplik biraz değer (gerçekten o kadar da kötü değil)? Kesinlikle. Ayrıca, çoklu çekirdeğin değişmezlikteki rolü oldukça küçüktür ... asıl fayda, akıl yürütmenin kolaylığıdır.
Mauricio Scheffer

@Mauricio - öyle diyorsan (Java'daki değişmezlik o kadar da kötü değil). 1998'den 2011'e kadar Java üzerinde çalıştıktan sonra, farklı olmaya yalvarırdım, basit kod tabanlarında önemsiz muaf değil. Bununla birlikte, insanların farklı deneyimleri vardır ve POV'mün öznellikten arınmış olmadığını kabul ediyorum. Çok üzgünüm, orada hemfikir değilim. Ancak, değişmezlik söz konusu olduğunda akıl yürütmenin en önemli şey olduğu konusunda hemfikirim.
luis.espinal

13

Mümkün olduğunda sınıfların değişmez olduğu konusunda ısrar etme aşamasından geçtim. Hemen hemen her şey için inşaatçılar vardı, değişmez diziler, vb. Sorunuzun cevabının basit olduğunu gördüm: Değişmez sınıflar hangi noktada yük haline geliyor? Çok çabuk. Bir şeyi serileştirmek istediğiniz anda, serileştirme yapabilmelisiniz, yani değişebilir olmalı; Bir ORM'yi kullanmak istediğinizde, çoğu değiştirilebilir özelliklerde ısrar ediyor. Ve bunun gibi.

Sonunda bu politikayı değiştirilebilir nesnelere değişmez arayüzlerle değiştirdim.

class NamedThing : INamedThing
{
    private string _name;    
    public NamedThing(string name)
    {
        _name = name;
    }    

    public NamedThing(NamedThing other)
    {
        this._name = other._name;
    }

    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }
}

interface INamedThing
{
    string Name { get; }
}

Şimdi nesnenin esnekliği var, ancak yine de arama koduna bu özellikleri düzenlememesi gerektiğini söyleyebilirsiniz.


8
Küçük tartışmalar bir yana, değişmez nesnelerin zorunlu bir dilde programlama yaparken popoda çok hızlı bir şekilde ağrı olabileceğini kabul ediyorum, ancak değişmez bir arayüzün aynı sorunu veya herhangi bir problemi gerçekten çözüp çözmediğinden emin değilim. Değişmez bir nesne kullanmanın asıl nedeni, nesneyi istediğiniz zaman herhangi bir yere atabileceğiniz ve asla devletinizi bozan başka biri hakkında endişelenmenize gerek olmamasıdır. Altta yatan nesne değişebiliyorsa, bu garantiye sahip değilsiniz, özellikle de onu değişebilir tutmanın nedeni, çeşitli şeylerin onu değiştirmesi gerektiğiydi.
Aaronaught

1
@Aaronaught - İlginç bir nokta. Sanırım bu gerçek bir korumanın ötesinde psikolojik bir şey. Ancak, son satırınızın yanlış bir öncülü vardır. Değişken kalmasının nedeni, çeşitli şeylerin onu somutlaştırması ve yansımayla doldurması gerektiği, somutlaştırıldıktan sonra mutasyona uğramaması gerektiğidir.
pdr

1
@Aaronaught: Aynı şekilde IComparable<T>eğer X.CompareTo(Y)>0ve Y.CompareTo(Z)>0, sonra garanti X.CompareTo(Z)>0. Arayüzlerin sözleşmeleri vardır . Sözleşme, IImmutableList<T>herhangi bir örnek dış dünyaya maruz bırakılmadan önce tüm öğelerin ve özelliklerin değerlerinin "taş olarak ayarlanması" gerektiğini belirtiyorsa, tüm yasal uygulamalar bunu yapacaktır. Hiçbir şey bir IComparableuygulamanın geçişsizliği ihlal etmesini engellemez , ancak bunu yapan uygulamalar gayri meşrudur. Bir SortedDictionarygayri meşru verildiğinde bir arıza varsa IComparable, ...
supercat

1
@Aaronaught: IReadOnlyList<T>(1) arayüz belgelerinde böyle bir gereklilik ve (2) en yaygın uygulama olan List<T>, salt okunur bile olmadığı için uygulamalarının değişmez olacağına neden güveneyim ki ? Terimlerim hakkında neyin belirsiz olduğunu tam olarak bilmiyorum: içindeki veriler okunabiliyorsa bir koleksiyon okunabilir. Bazı harici referanslar onu değiştirecek kod tarafından tutulmadıkça, içerilen verilerin değiştirilemeyeceğine söz verebilirse salt okunurdur . Değiştirilemeyeceğini garanti edemezse değişmez, nokta.
supercat

1
@supercat: Bu arada, Microsoft benimle aynı fikirde. Değişmez bir koleksiyon paketi yayınladılar ve hepsinin somut türler olduğunu fark ettiler, çünkü soyut bir sınıfın veya arayüzün gerçekten değişmez olduğunu asla garanti edemezsiniz.
Aaronaught

8

Bunun genel bir cevabı olduğunu düşünmüyorum. Bir sınıf ne kadar karmaşıksa, durum değişiklikleri hakkında muhakeme yapmak o kadar zor olur ve sınıfın yeni kopyalarını oluşturmak da o kadar pahalıdır. Dolayısıyla, bazı (kişisel) karmaşıklık seviyesinin üzerinde, bir sınıfı değişmez kılmak / tutmak çok acı verici olacaktır.

Not bir çok karmaşık sınıf veya uzun bir yöntem parametre listesi tasarım kokuyor bağımsız olarak değişmezlik, per se.

Bu nedenle, genellikle tercih edilen çözüm, böyle bir sınıfı, her biri kendi başına değişebilir veya değişmez hale getirilebilen birden fazla farklı sınıfa ayırmak olacaktır. Bu mümkün değilse, değiştirilebilir hale getirilebilir.


5

Tüm sabit alanlarınızı bir iç mekanda saklarsanız, kopyalama sorununu önleyebilirsiniz struct. Bu temel olarak hatıra modelinin bir varyasyonudur. Daha sonra bir kopya oluşturmak istediğinizde, hatayı kopyalamanız yeterlidir:

class MyClass
{
    struct Memento
    {
        public int field1;
        public string field2;
    }

    private readonly Memento memento;

    public MyClass(int field1, string field2)
    {
        this.memento = new Memento()
            {
                field1 = field1,
                field2 = field2
            };
    }

    private MyClass(Memento memento) // for copying
    {
        this.memento = memento;
    }

    public int Field1 { get { return this.memento.field1; } }
    public string Field2 { get { return this.memento.field2; } }

    public MyClass WithNewField1(int newField1)
    {
        Memento newMemento = this.memento;
        newMemento.field1 = newField1;
        return new MyClass(newMemento);
    }
}

Bence iç yapı gerekli değil. Bu, MemberwiseClone yapmanın başka bir yoludur.
Codism

@Codism - evet ve hayır. Klonlamak istemediğiniz diğer üyelere ihtiyaç duyabileceğiniz zamanlar vardır. Ya sahiplerinizden birinde tembel değerlendirme kullanıyor ve sonucu bir üyede önbelleğe alıyorsanız ne olurdu? Bir MemberwiseClone yaparsanız, önbelleğe alınan değeri kopyalarsınız, ardından üyelerinizden birini önbelleğe alınan değerin bağlı olduğu şekilde değiştirirsiniz. Durumu önbellekten ayırmak daha temizdir.
Scott Whitlock

İç yapının başka bir avantajından bahsetmeye değer olabilir: bir nesnenin durumunu diğer referansların bulunduğu bir nesneye kopyalamasını kolaylaştırır . OOP'taki yaygın bir belirsizlik kaynağı, nesne başvurusunu döndüren bir yöntemin, alıcının denetimi dışında değişebilecek bir nesnenin görünümünü döndürüp döndürmediğidir. Bir nesne başvurusu döndürmek yerine, bir yöntem çağırandan bir nesne başvurusunu kabul ederse ve durumu ona kopyalarsa, nesnenin sahipliği çok daha net olacaktır. Böyle bir yaklaşım özgürce devralınabilen
tiplerle

... değiştirilebilir veri tutucularla çok faydalı olabilir. Yaklaşım ayrıca "paralel" değişken ve değişmez sınıflara (soyut "okunabilir" bir tabandan türetilmiş) sahip olmayı ve yapıcılarının birbirinden veri kopyalayabilmesini kolaylaştırır.
supercat

3

Burada işte birkaç şey var. Değişmez veri kümeleri çok iş parçacıklı ölçeklenebilirlik için mükemmeldir. Temel olarak, hafızanızı biraz optimize edebilirsiniz, böylece bir parametre seti sınıfın bir örneğidir - her yerde. Nesneler asla değişmediği için üyelerine erişme konusunda senkronize etme konusunda endişelenmenize gerek yoktur. Bu iyi birşey. Bununla birlikte, işaret ettiğiniz gibi, nesne ne kadar karmaşıksa, biraz değişebilirliğe ihtiyacınız vardır. Bu çizgiler boyunca muhakeme ile başlardım:

  • Bir nesnenin yapabileceği herhangi bir iş nedeni var mı? kendi durumunu değiştirme? Örneğin, bir veritabanında depolanan bir kullanıcı nesnesi kimliğine göre benzersizdir, ancak zaman içinde durumunu değiştirebilmesi gerekir. Öte yandan bir ızgaradaki koordinatları değiştirdiğinizde, orijinal koordinat olmaktan çıkar ve bu nedenle koordinatları değişmez kılmak mantıklıdır. Dizelerle aynı.
  • Bazı özellikler hesaplanabilir mi? Kısacası, bir nesnenin yeni kopyasındaki diğer değerler, geçtiğiniz bazı temel değerlerin bir fonksiyonuysa, bunları yapıcıda veya talep üzerine hesaplayabilirsiniz. Bu, kopyalama veya oluşturma işlemlerinde aynı değerleri başlatabileceğiniz için bakım miktarını azaltır.
  • Yeni değişmez nesneyi kaç değer oluşturur? Bir noktada bir nesne yaratmanın karmaşıklığı önemsiz hale gelir ve bu noktada daha fazla nesne örneğine sahip olmak sorun haline gelebilir. Örnekler arasında değişmez ağaç yapıları, üçten fazla parametre geçirilmiş nesneler vb. Sayılabilir. Parametreler ne kadar fazla olursa parametrelerin sırasını bozma veya yanlış olanı boşaltma olasılığı da o kadar artar.

Yalnızca değiştirilemeyen nesneleri (Erlang gibi) destekleyen dillerde, değiştirilemez bir nesnenin durumunu değiştirmiş gibi görünen bir işlem varsa, sonuç, nesnenin güncellenmiş değere sahip yeni bir kopyasıdır. Örneğin, bir vektöre / listeye bir öğe eklediğinizde:

myList = lists:append([[1,2,3], [4,5,6]])
% myList is now [1,2,3,4,5,6]

Bu daha karmaşık nesnelerle çalışmanın akılcı bir yolu olabilir. Örneğin bir ağaç düğümü ekledikçe, sonuç eklenen düğüme sahip yeni bir ağaç olur. Yukarıdaki örnekteki yöntem yeni bir liste döndürür. Bu paragraftaki örnekte tree.add(newNode), eklenen düğüm ile yeni bir ağaç döndürülür. Kullanıcılar için çalışmak kolaylaşır. Kütüphane yazarları için, dil örtük kopyalamayı desteklemediğinde sıkıcı olur. Bu eşik sizin sabrınıza bağlı. Kütüphanenizin kullanıcıları için bulduğum en akıllıca limit yaklaşık üç ila dört parametre üst sınırıdır.


Eğer biri değişebilir bir nesne referansını bir değer olarak kullanmaya eğilimliyse [sahibi içinde hiçbir referans bulunmadığı ve hiçbir zaman açığa çıkmadığı anlamına gelirse ], istenen "değiştirilmiş" içeriği tutan yeni bir değişmez nesne oluşturmak, nesneyi doğrudan değiştirmeye eşdeğerdir, ama muhtemelen daha yavaştır. Değişken nesneler ise varlık olarak da kullanılabilir . Değişken nesnesi olmayan varlıklar gibi davranan şeyler nasıl yapılır?
supercat

0

Birden fazla final sınıfı üyeniz varsa ve bunların oluşturulması gereken tüm nesnelere maruz kalmasını istemiyorsanız, oluşturucu desenini kullanabilirsiniz:

class NamedThing
{
    private string _name;    
    private string _value;
    private NamedThing(string name, string value)
    {
        _name = name;
        _value = value;
    }    
    public NamedThing(NamedThing other)
    {
        this._name = other._name;
        this._value = other._value;
    }
    public string Name
    {
        get { return _name; }
    }

    public static class Builder {
        string _name;
        string _value;

        public void setValue(string value) {
            _value = value;
        }
        public void setName(string name) {
            _name = name;
        }
        public NamedThing newObject() {
            return new NamedThing(_name, _value);
        }
    }
}

avantajı, sadece farklı bir adın farklı bir değerine sahip yeni bir nesne kolayca oluşturabilmenizdir.


Ben senin inşaatçı statik olmak doğru olmadığını düşünüyorum. Başka bir iş parçacığı, bunları ayarladıktan sonra ancak aramadan önce statik adı veya statik değeri değiştirebilir newObject.
ErikE

Yalnızca builder sınıfı statiktir, ancak üyeleri değildir. Bu, oluşturduğunuz her oluşturucu için karşılık gelen değerlere sahip kendi üye kümelerine sahip oldukları anlamına gelir. Sınıfın statik olması gerekir, bu nedenle NamedThing
kapsayıcı

Ne dediğini görüyorum, sadece bununla ilgili bir sorun öngörüyorum çünkü bir geliştiricinin "başarı çukuruna düşmesine" yol açmıyor. Statik değişkenler kullanması, a Builderyeniden kullanılırsa, bahsettiğim şeyin gerçek bir riski olduğu anlamına gelir . Birisi çok fazla nesne inşa ediyor olabilir ve mülklerin çoğu aynı olduğundan, sadece yeniden kullanmak Builderve aslında onu bağımlılık enjekte edilen küresel bir singleton yapalım olduğuna karar verebilir ! Whoops. Başlıca hatalar tanıtıldı. Bence bu karışık somutlaştırılmış ve statik paterni kötüdür.
ErikE

1
@Salandur C # içindeki iç sınıflar her zaman Java iç sınıf anlamında "statik" tir.
Sebastian Redl

0

Peki bir sınıf hangi noktada değişmez olamayacak kadar karmaşık hale geliyor?

Bence, küçük sınıfları gösterdiğiniz diller gibi değişmez kılmak zahmete değmez . Burada küçük kullanıyorum, karmaşık değil , çünkü o sınıfa on alan ekleseniz ve gerçekten süslü operasyonlar yapsa bile, megabaytların gigabaytları bırakmasına izin vermeden kilobayt alacağından şüpheliyim, sınıf, harici yan etkilere neden olmaktan kaçınmak istiyorsa orijinali değiştirmekten kaçınmak için tüm nesnenin ucuz bir kopyasını oluşturabilir.

Kalıcı Veri Yapıları

Değişmezlik için kişisel kullanım bulduğum yerde, gösterdiğiniz sınıf örnekleri gibi, bir milyonu depolayan gibi bir sürü ufacık veri toplayan büyük, merkezi veri yapıları içindir NamedThings. Değişmez olan ve sadece salt okunur erişime izin veren bir arayüzün arkasında yer alan kalıcı bir veri yapısına ait olarak, kaba ait elemanlar, element sınıfı ( NamedThing) ile uğraşmak zorunda kalmadan değişmez hale gelir .

Ucuz Kopyalar

Kalıcı veri yapısı, bölgelerinin dönüştürülmesine ve benzersiz hale getirilmesine izin vererek, veri yapısının tamamını kopyalamak zorunda kalmadan orijinalde değişiklik yapılmasını önler. Bu onun gerçek güzelliği. Gigabayt bellek alan ve sadece bir megabaytın belleğini değiştiren bir veri yapısı giren yan etkilerden kaçınan işlevler yazmak istiyorsanız, girişe dokunmaktan kaçınmak ve yenisini döndürmek için tüm hilkat garibesini kopyalamanız gerekir. çıktı. Yan etkilerden kaçınmak için gigabaytları kopyalamak veya bu senaryoda yan etkilere neden olmak, iki hoş olmayan seçenek arasında seçim yapmanız gerektiğini gösterir.

Kalıcı bir veri yapısı ile, böyle bir işlevi yazmanıza ve tüm veri yapısının bir kopyasını yapmaktan kaçınmanıza izin verir, yalnızca işleviniz bir megabaytın belleğini dönüştürdüyse, çıktı için yalnızca bir megabayt ekstra bellek gerektirir.

Sorumluluk

Yüke gelince, en azından benim durumumda hemen bir tane var. Bu büyük veri yapısına dokunmadan dönüşümleri etkili bir şekilde ifade edebilmek için onları çağırdığımda, insanların oluşturduğu veya "geçici" olan inşaatçılara ihtiyacım var. Bunun gibi kod:

void transform_stuff(MutList<Stuff>& stuff, int first, int last)
{
     // Transform stuff in the range, [first, last).
     for (; first != last; ++first)
          transform(stuff[first]);
}

... o zaman şöyle yazılmalıdır:

ImmList<Stuff> transform_stuff(ImmList<Stuff> stuff, int first, int last)
{
     // Grab a "transient" (builder) list we can modify:
     TransientList<Stuff> transient(stuff);

     // Transform stuff in the range, [first, last)
     // for the transient list.
     for (; first != last; ++first)
          transform(transient[first]);

     // Commit the modifications to get and return a new
     // immutable list.
     return stuff.commit(transient);
}

Ancak bu iki ekstra kod satırı karşılığında, işlev artık aynı orijinal listeye sahip iş parçacıkları arasında arama yapmak güvenlidir, yan etkilere neden olmaz, vb. Ayrıca, bu işlemi geri alınamaz bir kullanıcı eylemi haline getirmeyi gerçekten kolaylaştırır. geri almak sadece eski listenin ucuz bir sığ kopyasını saklayabilir.

İstisna Güvenliği veya Hata Kurtarma

Herkes bu gibi bağlamlarda kalıcı veri yapılarından benim kadar faydalanamayabilir (VFX alanımda merkezi kavramlar olan geri alma sistemlerinde ve tahribatsız düzenlemede onlar için çok fazla kullanım buldum), ancak hemen hemen uygulanabilir bir şey göz önünde bulundurulması gereken herkes istisna güvenliği veya hata kurtarmadır .

Orijinal mutasyon işlevini istisna-güvenli hale getirmek istiyorsanız, en basit uygulamanın tüm listeyi kopyalamasını gerektiren geri alma mantığına ihtiyacı vardır :

void transform_stuff(MutList<Stuff>& stuff, int first, int last)
{
    // Make a copy of the whole massive gigabyte-sized list 
    // in case we encounter an exception and need to rollback
    // changes.
    MutList<Stuff> old_stuff = stuff;

    try
    {
         // Transform stuff in the range, [first, last).
         for (; first != last; ++first)
             transform(stuff[first]);
    }
    catch (...)
    {
         // If the operation failed and ran into an exception,
         // swap the original list with the one we modified
         // to "undo" our changes.
         stuff.swap(old_stuff);
         throw;
    }
}

Bu noktada, istisna-güvenli mutable versiyonu bir "oluşturucu" kullanarak değişmez versiyondan daha hesaplamalı olarak pahalı ve tartışmasız daha da zor. Ve bir çok C ++ geliştiricisi sadece istisna güvenliği ihmal ediyor ve belki de bu onların etki alanı için iyi, ancak benim durumumda, bir istisna durumunda bile kodumun doğru çalıştığından emin olmak istiyorum (istisnaları test etmek için kasıtlı olarak istisnalar atan testler bile yazmayı seviyorum) güvenlik), ve bu da bir işlevin neden olduğu herhangi bir yan etkiyi yarıya indirebilmem gerekiyor.

İstisna güvende olmak ve uygulamanız çökmeden ve yanmadan hatalardan incelikle kurtulmak istediğinizde, bir fonksiyonun bir hata / istisna durumunda neden olabileceği herhangi bir yan etkiyi geri almanız / geri almanız gerekir. Ve orada inşaatçı aslında hesaplama zamanıyla birlikte maliyetinden daha fazla programcı zaman kazandırabilir, çünkü: ...

Herhangi bir şeye neden olmayan bir fonksiyonda yan etkileri geri almak konusunda endişelenmenize gerek yok!

Temel soruya dönelim:

Değişmez sınıflar hangi noktada yük haline gelir?

Onlar her zaman değişebilirlik etrafında değişmezlikten daha fazla dönen dilde bir yüktür, bu yüzden faydaların maliyetlerden önemli ölçüde ağır bastığı yerlerde bunları kullanmalısınız. Ancak yeterince büyük veri yapıları için yeterince geniş bir seviyede, bunun değerli bir takas olduğu birçok durum olduğuna inanıyorum.

Ayrıca benimkinde, sadece birkaç değişmez veri türüm var ve bunların hepsi çok sayıda öğe (bir görüntünün / dokunun pikselleri, bir ECS'nin objeleri ve bileşenleri) ve köşeleri / kenarları / çokgenleri bir ağ).

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.