JavaScript modüllerinde Değer Nesneleri ile sağlam, sürdürülebilir ve test edilebilir kod yazın. Değişmez veri yapıları uygulayarak veri bütünlüğünü artırın.
JavaScript Modül Değer Nesnesi: Değişmez Veri Modelleme
Modern JavaScript geliştirmede, veri bütünlüğünü ve sürdürülebilirliği sağlamak çok önemlidir. Bunu başarmak için güçlü bir teknik, modüler JavaScript uygulamalarında Değer Nesnelerinden yararlanmaktır. Değer Nesneleri, özellikle değişmezlikle birleştirildiğinde, daha temiz, daha öngörülebilir ve test edilmesi daha kolay koda yol açan sağlam bir veri modelleme yaklaşımı sunar.
Değer Nesnesi Nedir?
Bir Değer Nesnesi, kavramsal bir değeri temsil eden küçük, basit bir nesnedir. Kimlikleriyle tanımlanan varlıkların aksine, Değer Nesneleri nitelikleriyle tanımlanır. İki Değer Nesnesi, nesne kimliklerinden bağımsız olarak nitelikleri eşitse eşit kabul edilir. Yaygın Değer Nesnesi örnekleri şunlardır:
- Para Birimi: Parasal bir değeri temsil eder (ör. 10 USD, 5 EUR).
- Tarih Aralığı: Bir başlangıç ve bitiş tarihini temsil eder.
- E-posta Adresi: Geçerli bir e-posta adresini temsil eder.
- Posta Kodu: Belirli bir bölge için geçerli bir posta kodunu temsil eder. (ör. ABD'de 90210, Birleşik Krallık'ta SW1A 0AA, Almanya'da 10115, Japonya'da 〒100-0001)
- Telefon Numarası: Geçerli bir telefon numarasını temsil eder.
- Koordinatlar: Coğrafi bir konumu (enlem ve boylam) temsil eder.
Bir Değer Nesnesinin temel özellikleri şunlardır:
- Değişmezlik: Bir Değer Nesnesi oluşturulduktan sonra durumu değiştirilemez. Bu, istenmeyen yan etkiler riskini ortadan kaldırır.
- Değere dayalı eşitlik: İki Değer Nesnesi, bellekte aynı nesne olmaları durumunda değil, değerleri eşitse eşittir.
- Kapsülleme: Değerin dahili temsili gizlenir ve erişim metotlar aracılığıyla sağlanır. Bu, doğrulamaya olanak tanır ve değerin bütünlüğünü sağlar.
Neden Değer Nesneleri Kullanmalıyız?
JavaScript uygulamalarınızda Değer Nesneleri kullanmak birçok önemli avantaj sunar:
- Geliştirilmiş Veri Bütünlüğü: Değer Nesneleri, oluşturma anında kısıtlamaları ve doğrulama kurallarını zorunlu kılarak yalnızca geçerli verilerin kullanılmasını sağlar. Örneğin, bir `EmailAddress` Değer Nesnesi, girilen dizenin gerçekten geçerli bir e-posta formatında olduğunu doğrulayabilir. Bu, hataların sisteminize yayılma olasılığını azaltır.
- Azaltılmış Yan Etkiler: Değişmezlik, Değer Nesnesinin durumunda istenmeyen değişiklik olasılığını ortadan kaldırarak daha öngörülebilir ve güvenilir koda yol açar.
- Basitleştirilmiş Test: Değer Nesneleri değişmez olduğundan ve eşitlikleri değere dayandığından, birim testi çok daha kolay hale gelir. Bilinen değerlere sahip Değer Nesneleri oluşturabilir ve bunları beklenen sonuçlarla karşılaştırabilirsiniz.
- Artırılmış Kod Anlaşılırlığı: Değer Nesneleri, alan kavramlarını açıkça temsil ederek kodunuzu daha anlamlı ve anlaşılır hale getirir. Ham dizeler veya sayılar göndermek yerine, `Currency` veya `PostalCode` gibi Değer Nesneleri kullanarak kodunuzun amacını daha net hale getirebilirsiniz.
- Geliştirilmiş Modülerlik: Değer Nesneleri, belirli bir değere ilişkin özel mantığı kapsülleyerek görevlerin ayrılmasını teşvik eder ve kodunuzu daha modüler hale getirir.
- Daha İyi İşbirliği: Standart Değer Nesneleri kullanmak, ekipler arasında ortak bir anlayışı teşvik eder. Örneğin, herkes bir 'Currency' nesnesinin neyi temsil ettiğini anlar.
JavaScript Modüllerinde Değer Nesneleri Uygulama
Şimdi, ES modüllerini kullanarak JavaScript'te Değer Nesnelerinin nasıl uygulanacağını, değişmezlik ve doğru kapsüllemeye odaklanarak inceleyelim.
Örnek: EmailAddress Değer Nesnesi
Basit bir `EmailAddress` Değer Nesnesi düşünelim. E-posta formatını doğrulamak için bir düzenli ifade kullanacağız.
```javascript // email-address.js const EMAIL_REGEX = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/; class EmailAddress { constructor(value) { if (!EmailAddress.isValid(value)) { throw new Error('Geçersiz e-posta adresi formatı.'); } // Özel özellik (closure kullanarak) let _value = value; this.getValue = () => _value; // Getter // Sınıf dışından değişikliği engelle Object.freeze(this); } getValue() { return this.value; } toString() { return this.getValue(); } static isValid(value) { return EMAIL_REGEX.test(value); } equals(other) { if (!(other instanceof EmailAddress)) { return false; } return this.getValue() === other.getValue(); } } export default EmailAddress; ```Açıklama:
- Modül Dışa Aktarımı: `EmailAddress` sınıfı bir modül olarak dışa aktarılır, bu da onu uygulamanızın farklı bölümlerinde yeniden kullanılabilir hale getirir.
- Doğrulama: Constructor, girilen e-posta adresini bir düzenli ifade (`EMAIL_REGEX`) kullanarak doğrular. E-posta geçersizse bir hata fırlatır. Bu, yalnızca geçerli `EmailAddress` nesnelerinin oluşturulmasını sağlar.
- Değişmezlik: `Object.freeze(this)`, `EmailAddress` nesnesi oluşturulduktan sonra üzerinde herhangi bir değişiklik yapılmasını engeller. Dondurulmuş bir nesneyi değiştirmeye çalışmak hatayla sonuçlanır. Ayrıca `_value` özelliğini gizlemek için closure kullanıyoruz, bu da sınıf dışından doğrudan erişimi imkansız hale getiriyor.
- `getValue()` Metodu: Bir `getValue()` metodu, temel e-posta adresi değerine kontrollü erişim sağlar.
- `toString()` Metodu: Bir `toString()` metodu, değer nesnesinin kolayca bir dizeye dönüştürülmesini sağlar.
- `isValid()` Statik Metodu: Statik bir `isValid()` metodu, sınıfın bir örneğini oluşturmadan bir dizenin geçerli bir e-posta adresi olup olmadığını kontrol etmenize olanak tanır.
- `equals()` Metodu: `equals()` metodu, iki `EmailAddress` nesnesini değerlerine göre karşılaştırarak eşitliğin nesne kimliğiyle değil, içerikle belirlenmesini sağlar.
Örnek Kullanım
```javascript // main.js import EmailAddress from './email-address.js'; try { const email1 = new EmailAddress('test@example.com'); const email2 = new EmailAddress('test@example.com'); const email3 = new EmailAddress('invalid-email'); // Bu bir hata fırlatacaktır console.log(email1.getValue()); // Çıktı: test@example.com console.log(email1.toString()); // Çıktı: test@example.com console.log(email1.equals(email2)); // Çıktı: true // email1'i değiştirmeye çalışmak bir hata fırlatacaktır (strict mode gereklidir) // email1.value = 'new-email@example.com'; // Hata: Cannot assign to read only property 'value' of object '#Gösterilen Faydalar
Bu örnek, Değer Nesnelerinin temel ilkelerini göstermektedir:
- Doğrulama: `EmailAddress` constructor'ı e-posta formatı doğrulamasını zorunlu kılar.
- Değişmezlik: `Object.freeze()` çağrısı değişikliği engeller.
- Değere Dayalı Eşitlik: `equals()` metodu, e-posta adreslerini değerlerine göre karşılaştırır.
İleri Düzey Konular
Typescript
Önceki örnekte saf JavaScript kullanılmış olsa da, TypeScript Değer Nesnelerinin geliştirilmesini ve sağlamlığını önemli ölçüde artırabilir. TypeScript, Değer Nesneleriniz için türler tanımlamanıza olanak tanıyarak derleme zamanı tür denetimi ve geliştirilmiş kod sürdürülebilirliği sağlar. İşte `EmailAddress` Değer Nesnesini TypeScript kullanarak nasıl uygulayabileceğiniz:
```typescript // email-address.ts const EMAIL_REGEX = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/; class EmailAddress { private readonly value: string; constructor(value: string) { if (!EmailAddress.isValid(value)) { throw new Error('Geçersiz e-posta adresi formatı.'); } this.value = value; Object.freeze(this); } getValue(): string { return this.value; } toString(): string { return this.value; } static isValid(value: string): boolean { return EMAIL_REGEX.test(value); } equals(other: EmailAddress): boolean { return this.value === other.getValue(); } } export default EmailAddress; ```TypeScript ile temel iyileştirmeler:
- Tür Güvenliği: `value` özelliği açıkça `string` olarak tiplenir ve constructor yalnızca dize geçirilmesini zorunlu kılar.
- Salt Okunur Özellikler: `readonly` anahtar kelimesi, `value` özelliğinin yalnızca constructor'da atanabilmesini sağlayarak değişmezliği daha da güçlendirir.
- Geliştirilmiş Kod Tamamlama ve Hata Tespiti: TypeScript, daha iyi kod tamamlama sağlar ve geliştirme sırasında türle ilgili hataların yakalanmasına yardımcı olur.
Fonksiyonel Programlama Teknikleri
Değer Nesnelerini fonksiyonel programlama ilkelerini kullanarak da uygulayabilirsiniz. Bu yaklaşım genellikle değişmez veri yapıları oluşturmak ve işlemek için fonksiyonların kullanılmasını içerir.
```javascript // currency.js import { isNil, isNumber, isString } from 'lodash-es'; function Currency(amount, code) { if (!isNumber(amount)) { throw new Error('Miktar bir sayı olmalıdır'); } if (!isString(code) || code.length !== 3) { throw new Error('Kod 3 harfli bir dize olmalıdır'); } const _amount = amount; const _code = code.toUpperCase(); return Object.freeze({ getAmount: () => _amount, getCode: () => _code, toString: () => `${_code} ${_amount}`, equals: (other) => { if (isNil(other) || typeof other.getAmount !== 'function' || typeof other.getCode !== 'function') { return false; } return other.getAmount() === _amount && other.getCode() === _code; } }); } export default Currency; // Örnek // const price = Currency(19.99, 'USD'); ```Açıklama:
- Fabrika Fonksiyonu: `Currency` fonksiyonu, değişmez bir nesne oluşturan ve döndüren bir fabrika görevi görür.
- Closure'lar: `_amount` ve `_code` değişkenleri, fonksiyonun kapsamı içinde yer alır, bu da onları özel ve dışarıdan erişilemez hale getirir.
- Değişmezlik: `Object.freeze()`, döndürülen nesnenin değiştirilememesini sağlar.
Serileştirme ve Deserileştirme
Değer Nesneleriyle çalışırken, özellikle dağıtık sistemlerde veya veri depolarken, onları sık sık serileştirmeniz (JSON gibi bir dize formatına dönüştürmeniz) ve deserileştirmeniz (bir dize formatından tekrar Değer Nesnesine dönüştürmeniz) gerekir. JSON serileştirmesi kullanırken, genellikle değer nesnesini temsil eden ham değerleri ( `string` temsili, `number` temsili vb.) alırsınız.
Deserileştirme yaparken, doğrulamayı ve değişmezliği zorunlu kılmak için Değer Nesnesi örneğini her zaman constructor'ını kullanarak yeniden oluşturduğunuzdan emin olun.
```javascript // Serileştirme const email = new EmailAddress('test@example.com'); const emailJSON = JSON.stringify(email.getValue()); // Temel değeri serileştir console.log(emailJSON); // Çıktı: "test@example.com" // Deserileştirme const deserializedEmail = new EmailAddress(JSON.parse(emailJSON)); // Değer Nesnesini yeniden oluştur console.log(deserializedEmail.getValue()); // Çıktı: test@example.com ```Gerçek Dünya Örnekleri
Değer Nesneleri çeşitli senaryolarda uygulanabilir:
- E-ticaret: Ürün fiyatlarını bir `Currency` Değer Nesnesi kullanarak temsil etmek ve tutarlı para birimi yönetimi sağlamak. Ürün SKU'larını bir `SKU` Değer Nesnesi ile doğrulamak.
- Finansal Uygulamalar: Parasal tutarları ve hesap numaralarını `Money` ve `AccountNumber` Değer Nesneleri ile yönetmek, doğrulama kurallarını zorunlu kılmak ve hataları önlemek.
- Coğrafi Uygulamalar: Koordinatları bir `Coordinates` Değer Nesnesi ile temsil etmek, enlem ve boylam değerlerinin geçerli aralıklarda olmasını sağlamak. Ülkeleri bir `CountryCode` Değer Nesnesi ile temsil etmek (ör. "US", "GB", "DE", "JP", "BR").
- Kullanıcı Yönetimi: E-posta adreslerini, telefon numaralarını ve posta kodlarını özel Değer Nesneleri kullanarak doğrulamak.
- Lojistik: Gönderim adreslerini bir `Address` Değer Nesnesi ile yönetmek, gerekli tüm alanların mevcut ve geçerli olmasını sağlamak.
Kodun Ötesindeki Faydalar
- Geliştirilmiş İşbirliği: Değer nesneleri, ekibiniz ve projeniz içinde ortak bir kelime dağarcığı tanımlar. Herkes bir `PostalCode` veya bir `PhoneNumber` nesnesinin neyi temsil ettiğini anladığında, işbirliği önemli ölçüde gelişir.
- Daha Kolay İşe Alım: Yeni ekip üyeleri, her bir değer nesnesinin amacını ve kısıtlamalarını anlayarak alan modelini hızla kavrayabilir.
- Azaltılmış Bilişsel Yük: Karmaşık mantığı ve doğrulamayı değer nesneleri içinde kapsülleyerek, geliştiricilerin daha üst düzey iş mantığına odaklanmasını sağlarsınız.
Değer Nesneleri İçin En İyi Uygulamalar
- Küçük ve odaklı tutun: Bir Değer Nesnesi, tek ve iyi tanımlanmış bir kavramı temsil etmelidir.
- Değişmezliği zorunlu kılın: Değer Nesnesinin durumunun oluşturulduktan sonra değiştirilmesini önleyin.
- Değere dayalı eşitlik uygulayın: İki Değer Nesnesinin değerleri eşitse eşit kabul edilmesini sağlayın.
- Bir `toString()` metodu sağlayın: Bu, Değer Nesnelerini günlük kaydı ve hata ayıklama için dize olarak temsil etmeyi kolaylaştırır.
- Kapsamlı birim testleri yazın: Değer Nesnelerinizin doğrulamasını, eşitliğini ve değişmezliğini kapsamlı bir şekilde test edin.
- Anlamlı isimler kullanın: Değer Nesnesinin temsil ettiği kavramı açıkça yansıtan isimler seçin (ör. `EmailAddress`, `Currency`, `PostalCode`).
Sonuç
Değer Nesneleri, JavaScript uygulamalarında verileri modellemek için güçlü bir yol sunar. Değişmezliği, doğrulamayı ve değere dayalı eşitliği benimseyerek daha sağlam, sürdürülebilir ve test edilebilir kod oluşturabilirsiniz. İster küçük bir web uygulaması ister büyük ölçekli bir kurumsal sistem oluşturuyor olun, mimarinize Değer Nesnelerini dahil etmek yazılımınızın kalitesini ve güvenilirliğini önemli ölçüde artırabilir. Bu nesneleri düzenlemek ve dışa aktarmak için modülleri kullanarak, daha modüler ve iyi yapılandırılmış bir kod tabanına katkıda bulunan yüksek düzeyde yeniden kullanılabilir bileşenler oluşturursunuz. Değer Nesnelerini benimsemek, küresel bir kitle için daha temiz, daha güvenilir ve anlaşılması daha kolay JavaScript uygulamaları oluşturmaya yönelik önemli bir adımdır.