Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
C# nesne odaklı bir programlama dilidir. Nesne odaklı programlamanın dört temel ilkesi şunlardır:
- Soyutlama Sistemin soyut bir gösterimini tanımlamak için ilgili öznitelikleri ve varlıkların etkileşimlerini sınıf olarak modelleme.
- Kapsülleme Bir nesnenin iç durumunu ve işlevselliğini gizleme ve yalnızca genel işlev kümesi üzerinden erişime izin verme.
- Miras Mevcut soyutlamaları temel alan yeni soyutlamalar oluşturabilme.
- Polimorfizm Birden çok soyutlamada devralınan özellikleri veya yöntemleri farklı şekillerde uygulayabilme.
Önceki öğreticide, hem soyutlama hem de kapsülleme gördüğünüz sınıflara giriş konusu işlendi. sınıfı, BankAccount
banka hesabı kavramı için bir soyutlama sağladı. sınıfını kullanan BankAccount
kodlardan herhangi birini etkilemeden uygulamasını değiştirebilirsiniz.
BankAccount
Hem hem de Transaction
sınıfları, koddaki bu kavramları açıklamak için gereken bileşenlerin kapsüllemesini sağlar.
Bu öğreticide, yeni özellikler eklemek için devralma ve çok biçimlilikten yararlanmak için bu uygulamayı genişleteceksiniz. Ayrıca, önceki öğreticide BankAccount
öğrendiğiniz soyutlama ve kapsülleme tekniklerinden yararlanarak sınıfına özellikler ekleyeceksiniz.
Farklı hesap türleri oluşturma
Bu programı derledikten sonra, bu programa özellik ekleme istekleri alırsınız. Yalnızca bir banka hesabı türünün olduğu durumlarda harika çalışır. Zaman içinde değişiklik gerekiyor ve ilgili hesap türleri isteniyor:
- Her ayın sonunda faiz kazandıran bir hesap.
- Negatif bakiye verebilen, ancak bakiye olduğunda her ay faiz işletilen bir kredi limiti.
- Tek bir depozito ile başlayan ve yalnızca ödenebilen ön ödemeli bir hediye kartı hesabı. Her ayın başında bir kez yeniden doldurulabilir.
Bu farklı hesapların tümü önceki öğreticide tanımlanan sınıfa BankAccount
benzer. Bu kodu kopyalayabilir, sınıfları yeniden adlandırabilir ve değişiklikler yapabilirsiniz. Bu teknik kısa vadede işe yarasa da zaman içinde daha fazla iş olacaktır. Tüm değişiklikler etkilenen tüm sınıflara kopyalanır.
Bunun yerine, önceki öğreticide oluşturulan sınıftan BankAccount
yöntemleri ve verileri devralan yeni banka hesabı türleri oluşturabilirsiniz. Bu yeni sınıflar sınıfı her tür için gereken belirli bir davranışla genişletebilir BankAccount
:
public class InterestEarningAccount : BankAccount
{
}
public class LineOfCreditAccount : BankAccount
{
}
public class GiftCardAccount : BankAccount
{
}
Bu sınıfların her biri, sınıfı olan paylaşılan temel sınıfından ortak davranışı BankAccount
.
Türetilmiş sınıfların her birinde yeni ve farklı işlevler için uygulamaları yazın. Bu türetilmiş sınıflar zaten sınıfında tanımlanan tüm davranışlara BankAccount
sahiptir.
Her yeni sınıfı farklı bir kaynak dosyada oluşturmak iyi bir uygulamadır. Visual Studio'da projeye sağ tıklayabilir ve sınıf ekle'yi seçerek yeni bir dosyaya yeni bir sınıf ekleyebilirsiniz. Visual Studio Code'daDosya'yı ve ardından Yeni'yi seçerek yeni bir kaynak dosya oluşturun. Her iki araçta da dosyayı sınıfıyla eşleşecek şekilde adlandırın: InterestEarningAccount.cs, LineOfCreditAccount.cs ve GiftCardAccount.cs.
Önceki örnekte gösterildiği gibi sınıfları oluşturduğunuzda, türetilmiş sınıflarınızdan hiçbirinin derlenmediğini görürsünüz. Oluşturucu, bir nesneyi başlatmakla sorumludur. Türetilmiş bir sınıf oluşturucu, türetilmiş sınıfı başlatmalı ve türetilmiş sınıfa dahil edilen temel sınıf nesnesini başlatma yönergeleri sağlamalıdır. Düzgün başlatma normalde fazladan kod olmadan gerçekleşir.
BankAccount
sınıfı aşağıdaki imzaya sahip bir ortak oluşturucu bildirir:
public BankAccount(string name, decimal initialBalance)
Bir oluşturucuyu kendiniz tanımladığınızda derleyici varsayılan bir oluşturucu oluşturmaz. Bu, türetilmiş her sınıfın açıkça bu oluşturucuyu çağırması gerektiği anlamına gelir. Temel sınıf oluşturucusuna argümanlar iletebilen bir yapıcı tanımlarsınız. Aşağıdaki kod, InterestEarningAccount
için oluşturucusunu gösterir.
public InterestEarningAccount(string name, decimal initialBalance) : base(name, initialBalance)
{
}
Bu yeni oluşturucunun parametreleri, temel sınıf oluşturucusunun parametre türü ve adlarıyla eşleşmektedir. Bir temel sınıf oluşturucusunun çağrısını belirtmek için söz dizimini kullanırsınız : base()
. Bazı sınıflar birden çok oluşturucu tanımlar ve bu söz dizimi, çağırdığınız temel sınıf oluşturucuyu seçmenizi sağlar. Oluşturucuları güncelleştirdikten sonra, türetilmiş sınıfların her biri için kodu geliştirebilirsiniz. Yeni sınıfların gereksinimleri aşağıdaki gibi belirtilebilir:
- Bir faiz kazanç hesabı:
- Ay sonu bakiyesinden 2% kredi alacak.
- Kredi limiti
- Negatif bakiyeye sahip olabilir, ancak mutlak değerde kredi limitinden büyük olamaz.
- Ay sonu bakiyesinin 0 olmadığı her ay bir faiz ücreti uygulanır.
- Kredi limitini aşan her para çekme için bir ücret uygulanır.
- Hediye kartı hesabı:
- Her ay, ayın son günü belirli bir miktarla yeniden doldurulabilir.
Bu hesap türlerinin üçünün de her ayın sonunda gerçekleşen bir eylemi olduğunu görebilirsiniz. Ancak, her hesap türü farklı görevler yapar. Bu kodu uygulamak için çok biçimlilik kullanırsınız.
virtual
sınıfı içinde tek bir BankAccount
yöntemi oluşturun.
public virtual void PerformMonthEndTransactions() { }
Yukarıdaki kod, türetilmiş bir sınıfın farklı bir uygulama sağlayabileceği temel sınıfta bir yöntemi bildirmek için virtual
anahtar sözcüğünü nasıl kullandığınızı gösterir.
virtual
yöntemi, türetilmiş herhangi bir sınıfın yeniden uygulamayı seçebileceği bir yöntemdir. Türetilmiş sınıflar, yeni uygulamayı tanımlamak için anahtar sözcüğünü kullanır override
. Genellikle bunu "temel sınıf uygulamasını geçersiz kılma" olarak adlandırın.
virtual
anahtar sözcüğü, türetilmiş sınıfların davranışı geçersiz kılabileceğini belirtir. Ayrıca türetilmiş sınıfların davranışı geçersiz kılması gereken yöntemleri de bildirebilirsiniz abstract
. Temel sınıf bir yöntem için abstract
bir uygulama sağlamaz. Ardından, oluşturduğunuz yeni sınıflardan ikisi için uygulamayı tanımlamanız gerekir. ile InterestEarningAccount
başlayın:
public override void PerformMonthEndTransactions()
{
if (Balance > 500m)
{
decimal interest = Balance * 0.02m;
MakeDeposit(interest, DateTime.Now, "apply monthly interest");
}
}
aşağıdaki kodu öğesine LineOfCreditAccount
ekleyin. Kod, hesaptan çekilen pozitif bir faiz ücretini hesaplamak için bakiyeyi geçersiz hale getirir:
public override void PerformMonthEndTransactions()
{
if (Balance < 0)
{
// Negate the balance to get a positive interest charge:
decimal interest = -Balance * 0.07m;
MakeWithdrawal(interest, DateTime.Now, "Charge monthly interest");
}
}
sınıfı, GiftCardAccount
ay sonu işlevselliğini uygulamak için iki değişikliğe ihtiyaç duyar. İlk olarak, oluşturucuyu her ay eklenecek isteğe bağlı bir miktar içerecek şekilde değiştirin:
private readonly decimal _monthlyDeposit = 0m;
public GiftCardAccount(string name, decimal initialBalance, decimal monthlyDeposit = 0) : base(name, initialBalance)
=> _monthlyDeposit = monthlyDeposit;
Oluşturucu, monthlyDeposit
için varsayılan bir değer sağlar, böylece arayanlar aylık para yatırma işlemi olmadan 0
değerini atlayabilir. Ardından, oluşturucuda PerformMonthEndTransactions
sıfır olmayan bir değere ayarlanmışsa aylık depozito eklemek için yöntemini geçersiz kılın:
public override void PerformMonthEndTransactions()
{
if (_monthlyDeposit != 0)
{
MakeDeposit(_monthlyDeposit, DateTime.Now, "Add monthly deposit");
}
}
Geçersiz kılma, oluşturucuda ayarlanan aylık depozitoyu uygular. Aşağıdaki kodu Main
yöntemine GiftCardAccount
ve InterestEarningAccount
için bu değişiklikleri test etmek üzere ekleyin:
var giftCard = new GiftCardAccount("gift card", 100, 50);
giftCard.MakeWithdrawal(20, DateTime.Now, "get expensive coffee");
giftCard.MakeWithdrawal(50, DateTime.Now, "buy groceries");
giftCard.PerformMonthEndTransactions();
// can make additional deposits:
giftCard.MakeDeposit(27.50m, DateTime.Now, "add some additional spending money");
Console.WriteLine(giftCard.GetAccountHistory());
var savings = new InterestEarningAccount("savings account", 10000);
savings.MakeDeposit(750, DateTime.Now, "save some money");
savings.MakeDeposit(1250, DateTime.Now, "Add more savings");
savings.MakeWithdrawal(250, DateTime.Now, "Needed to pay monthly bills");
savings.PerformMonthEndTransactions();
Console.WriteLine(savings.GetAccountHistory());
Sonuçları doğrulayın. Şimdi, için LineOfCreditAccount
benzer bir test kodu kümesi ekleyin:
var lineOfCredit = new LineOfCreditAccount("line of credit", 0);
// How much is too much to borrow?
lineOfCredit.MakeWithdrawal(1000m, DateTime.Now, "Take out monthly advance");
lineOfCredit.MakeDeposit(50m, DateTime.Now, "Pay back small amount");
lineOfCredit.MakeWithdrawal(5000m, DateTime.Now, "Emergency funds for repairs");
lineOfCredit.MakeDeposit(150m, DateTime.Now, "Partial restoration on repairs");
lineOfCredit.PerformMonthEndTransactions();
Console.WriteLine(lineOfCredit.GetAccountHistory());
Yukarıdaki kodu ekleyip programı çalıştırdığınızda aşağıdaki hataya benzer bir şey görürsünüz:
Unhandled exception. System.ArgumentOutOfRangeException: Amount of deposit must be positive (Parameter 'amount')
at OOProgramming.BankAccount.MakeDeposit(Decimal amount, DateTime date, String note) in BankAccount.cs:line 42
at OOProgramming.BankAccount..ctor(String name, Decimal initialBalance) in BankAccount.cs:line 31
at OOProgramming.LineOfCreditAccount..ctor(String name, Decimal initialBalance) in LineOfCreditAccount.cs:line 9
at OOProgramming.Program.Main(String[] args) in Program.cs:line 29
Uyarı
Gerçek çıkış, projenin bulunduğu klasörün tam yolunu içerir. Klasör adları kısa olduğu için atlandı. Ayrıca, kod biçiminize bağlı olarak satır numaraları biraz farklı olabilir.
İlk bakiyenin 0'dan BankAccount
büyük olması gerektiğini varsaydığından bu kod başarısız olur.
BankAccount
sınıfının bir diğer varsayımı, bakiyenin negatife olamaz olduğudur. Bunun yerine, hesabı aşan herhangi bir para çekme işlemi reddedilir. Bu varsayımların her ikisinin de değişmesi gerekir. Kredili mevduat hesabı 0'dan başlar ve genellikle negatif bakiye verir. Ayrıca, bir müşteri çok fazla borç alırsa ücrete tabidir. İşlem kabul edilir, yalnızca maliyeti daha yüksektir. İlk kural, oluşturucuya en düşük bakiyeyi BankAccount
belirten isteğe bağlı bir bağımsız değişken eklenerek uygulanabilir. Varsayılan değer: 0
. İkinci kural, türetilmiş sınıfların varsayılan algoritmayı değiştirmesini sağlayan bir mekanizma gerektirir. Bir anlamda, temel sınıfı türetilmiş türe aşırı yükleme olduğunda ne olması gerektiğini "sorar". Varsayılan davranış, bir özel durum oluşturarak işlemi reddetmektir.
İsteğe bağlı minimumBalance
bir parametre içeren ikinci bir oluşturucu ekleyerek başlayalım. Bu yeni oluşturucu, var olan oluşturucu tarafından yapılan tüm eylemleri gerçekleştirir. Ayrıca, minimum balance özelliğini ayarlar. Mevcut oluşturucunun gövdesini kopyalayabilirsiniz, ancak bu, gelecekte değiştirilmesi gereken iki konum anlamına gelir. Bunun yerine, bir yapıcı zincirleme kullanarak bir yapıcının başka bir yapıcıyı çağırmasını sağlayabilirsiniz. Aşağıdaki kod iki oluşturucuyu ve yeni ek alanı gösterir:
private readonly decimal _minimumBalance;
public BankAccount(string name, decimal initialBalance) : this(name, initialBalance, 0) { }
public BankAccount(string name, decimal initialBalance, decimal minimumBalance)
{
Number = s_accountNumberSeed.ToString();
s_accountNumberSeed++;
Owner = name;
_minimumBalance = minimumBalance;
if (initialBalance > 0)
MakeDeposit(initialBalance, DateTime.Now, "Initial balance");
}
Yukarıdaki kod iki yeni tekniği gösterir. İlk olarak, minimumBalance
alanı readonly
olarak işaretlenir. Bu, nesne oluşturulduğunda değerin değiştirilemeyeceği anlamına gelir. bir BankAccount
oluşturulduktan minimumBalance
sonra değiştirilemez. İkincisi, iki parametreli yapıcı, : this(name, initialBalance, 0) { }
'yi uygulaması olarak kullanır. ifadesi : this()
, üç parametresi olan diğer oluşturucuyu çağırır. Bu teknik, istemci kodu birçok oluşturucudan birini seçebilse bile bir nesneyi başlatmak için tek bir uygulamaya sahip olmanıza olanak tanır.
Bu uygulama, ilk bakiye MakeDeposit
değerinden büyükse yalnızca 0
'i çağırır. Bu, depozitoların pozitif olması gerektiği kuralını korur, ancak kredi hesabının bir 0
bakiyeyle açılmasını sağlar.
Artık BankAccount
sınıfının minimum bakiye için salt okunur bir alanı olduğuna göre, son değişiklik 0
yöntemindeki sabit kodu minimumBalance
'den MakeWithdrawal
'ye değiştirmektir.
if (Balance - amount < _minimumBalance)
sınıfını BankAccount
genişlettik sonra, aşağıdaki kodda LineOfCreditAccount
gösterildiği gibi oluşturucuyu yeni temel oluşturucuyu çağıracak şekilde değiştirebilirsiniz:
public LineOfCreditAccount(string name, decimal initialBalance, decimal creditLimit) : base(name, initialBalance, -creditLimit)
{
}
Dikkat edin: LineOfCreditAccount
oluşturucu, creditLimit
parametresinin işaretini değiştirerek minimumBalance
parametresinin anlamıyla eşleşmesini sağlar.
Farklı overdraft kuralları
Eklenecek son özellik, işlemi reddetmek yerine kredi limitini aşmak için ücret tahsil etme olanağı sağlar LineOfCreditAccount
.
Bir teknik, gerekli davranışı uyguladığınız bir sanal işlev tanımlamaktır.
BankAccount
sınıf, yöntemi iki yöntem haline MakeWithdrawal
olarak yeniden düzenledi. Yeni yöntem, para çekme işlemi minimum bakiyenin altına indiğinde belirtilen eylemi yapar. Mevcut MakeWithdrawal
yöntem aşağıdaki koda sahiptir:
public void MakeWithdrawal(decimal amount, DateTime date, string note)
{
if (amount <= 0)
{
throw new ArgumentOutOfRangeException(nameof(amount), "Amount of withdrawal must be positive");
}
if (Balance - amount < _minimumBalance)
{
throw new InvalidOperationException("Not sufficient funds for this withdrawal");
}
var withdrawal = new Transaction(-amount, date, note);
_allTransactions.Add(withdrawal);
}
Şunu aşağıdaki kodla değiştirin:
public void MakeWithdrawal(decimal amount, DateTime date, string note)
{
if (amount <= 0)
{
throw new ArgumentOutOfRangeException(nameof(amount), "Amount of withdrawal must be positive");
}
Transaction? overdraftTransaction = CheckWithdrawalLimit(Balance - amount < _minimumBalance);
Transaction? withdrawal = new(-amount, date, note);
_allTransactions.Add(withdrawal);
if (overdraftTransaction != null)
_allTransactions.Add(overdraftTransaction);
}
protected virtual Transaction? CheckWithdrawalLimit(bool isOverdrawn)
{
if (isOverdrawn)
{
throw new InvalidOperationException("Not sufficient funds for this withdrawal");
}
else
{
return default;
}
}
Eklenen yöntem, protected
yalnızca türetilmiş sınıflardan çağrılabileceği anlamına gelir. Bu bildirim, diğer istemcilerin yöntemini çağırmasını engeller. Ayrıca virtual
türetilmiş sınıfların davranışı değiştirebilmesi için de böyledir. Dönüş türü Transaction?
. Bu ?
açıklama, yöntem null
döndürebileceğini gösterir. Para çekme limiti aşıldığında ücret tahsil etmek için aşağıdaki uygulamayı LineOfCreditAccount
ekleyin:
protected override Transaction? CheckWithdrawalLimit(bool isOverdrawn) =>
isOverdrawn
? new Transaction(-20, DateTime.Now, "Apply overdraft fee")
: default;
Hesapta yetersiz bakiye olduğunda, sistem ücretli bir işlem gerçekleştirir. Para çekme işlemi sınırı aşmazsa yöntem bir null
işlem döndürür. Bu bir ücret olmadığını gösterir.
Main
yöntemine Program
sınıfındaki aşağıdaki kodu ekleyerek bu değişiklikleri test edin:
var lineOfCredit = new LineOfCreditAccount("line of credit", 0, 2000);
// How much is too much to borrow?
lineOfCredit.MakeWithdrawal(1000m, DateTime.Now, "Take out monthly advance");
lineOfCredit.MakeDeposit(50m, DateTime.Now, "Pay back small amount");
lineOfCredit.MakeWithdrawal(5000m, DateTime.Now, "Emergency funds for repairs");
lineOfCredit.MakeDeposit(150m, DateTime.Now, "Partial restoration on repairs");
lineOfCredit.PerformMonthEndTransactions();
Console.WriteLine(lineOfCredit.GetAccountHistory());
Programı çalıştırın ve sonuçları denetleyin.
Özet
Takıldıysanız GitHub depomuzda bu öğreticinin kaynağını görebilirsiniz.
Bu öğreticide Object-Oriented programlamada kullanılan tekniklerin birçoğu gösterilmiştir:
- Farklı hesap türlerinin her biri için sınıflar tanımlarken Soyutlama kullandınız. Bu sınıflar, bu tür bir hesabın davranışını açıklamaktadır.
- Her sınıfta birçok ayrıntıyı tutarken Kapsülleme kullandınız.
- Önceden oluşturulmuş sınıfındaki uygulamadan yararlandığınızda kodu kaydetmek için
BankAccount
kullandınız. - Çok biçimlilik kullandınız ve türetilmiş sınıfların bu hesap türü için belirli bir davranış oluşturmak amacıyla geçersiz kılabileceği yöntemler oluşturuldu.