클래스 및 개체를 사용한 개체 지향 프로그래밍 살펴보기
이 자습서에서는 콘솔 애플리케이션을 빌드하고 C# 언어의 일부인 기본 개체 지향 기능을 확인합니다.
필수 조건
- Windows용으로는 Visual Studio를 권장합니다. Visual Studio 다운로드 페이지에서 무료 버전을 다운로드할 수 있습니다. Visual Studio에는 .NET SDK가 포함되어 있습니다.
- C# DevKit과 함께 Visual Studio Code 편집기를 사용할 수도 있습니다. 최신 .NET SDK를 별도로 설치해야 합니다.
- 다른 편집기를 선호하는 경우 최신 .NET SDK를 설치해야 합니다.
애플리케이션 만들기
터미널 창을 사용하여 클래스라는 디렉터리를 만듭니다. 거기에 애플리케이션을 빌드할 것입니다. 해당 디렉터리로 변경하고 콘솔 창에 dotnet new console
을 입력합니다. 이 명령은 애플리케이션을 만듭니다. Program.cs를 엽니다. 다음과 같이 표시됩니다.
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
이 자습서에서는 은행 계좌를 나타내는 새로운 형식을 만듭니다. 일반적으로 개발자는 여러 텍스트 파일에 각 클래스를 정의합니다. 그러면 프로그램의 크기가 커질 때 쉽게 관리할 수 있습니다. 클래스 디렉터리에 BankAccount.cs라는 이름의 새 파일을 만듭니다.
이 파일에는 은행 계좌의 정의가 포함됩니다. 개체 지향 프로그래밍은 클래스 형태로 형식을 생성하여 코드를 구성합니다. 이러한 클래스에는 특정 엔티티를 나타내는 코드가 포함됩니다. BankAccount
클래스는 은행 계좌를 나타냅니다. 코드는 메서드 및 속성을 통해 특정 작업을 구현합니다. 이 자습서에서 은행 계좌는 다음 동작을 지원합니다.
- 은행 계좌를 고유하게 식별하는 10자리 숫자가 있습니다.
- 소유자의 이름을 저장하는 문자열이 있습니다.
- 잔액을 검색할 수 있습니다.
- 예금을 허용합니다.
- 인출을 허용합니다.
- 초기 잔액은 양수여야 합니다.
- 인출로 인해 잔액이 음수가 될 수 없습니다.
은행 계좌 형식 정의
동작을 정의하는 클래스의 기본 사항을 만들어 시작할 수 있습니다. File:New 명령을 사용하여 새 파일을 만듭니다. 파일의 이름을 BankAccount.cs로 지정합니다. BankAccount.cs 파일에 다음 코드를 추가합니다.
namespace Classes;
public class BankAccount
{
public string Number { get; }
public string Owner { get; set; }
public decimal Balance { get; }
public void MakeDeposit(decimal amount, DateTime date, string note)
{
}
public void MakeWithdrawal(decimal amount, DateTime date, string note)
{
}
}
계속하기 전에 빌드한 내용을 살펴보겠습니다. namespace
선언은 코드를 논리적으로 구성하는 방법을 제공합니다. 이 자습서는 비교적 작으므로 하나의 네임스페이스에 모든 코드를 넣습니다.
public class BankAccount
(은)는 생성하는 클래스 또는 형식을 정의합니다. 클래스 선언 뒤에 오는 {
및 }
의 모든 항목은 클래스의 상태와 동작을 정의합니다. BankAccount
클래스의 멤버가 다섯 개 있습니다. 첫 번째 세 개는 속성입니다. 속성은 데이터 요소이며 유효성 검사 또는 기타 규칙을 적용하는 코드가 있을 수 있습니다. 마지막 두 개는 메서드입니다. 메서드는 단일 함수를 수행하는 코드 블록입니다. 각 멤버의 이름을 읽으면 사용자 또는 다른 개발자가 클래스가 수행하는 작업을 이해하기에 충분한 정보를 제공해야 합니다.
새 계좌 개설
구현할 첫 번째 기능은 은행 계좌 개설 기능입니다. 고객이 계좌를 개설할 때 초기 잔액과 해당 계좌 소유자에 대한 정보를 제공해야 합니다.
BankAccount
형식의 새 개체를 생성하는 것은 해당 값을 지정하는 생성자를 정의하는 것입니다. 생성자는 클래스와 이름이 같은 멤버입니다. 해당 클래스 형식의 개체를 초기화하는 데 사용됩니다. BankAccount
형식에 다음 생성자를 추가합니다. MakeDeposit
선언 위에 다음 코드를 배치합니다.
public BankAccount(string name, decimal initialBalance)
{
this.Owner = name;
this.Balance = initialBalance;
}
앞의 코드는 this
한정자를 포함하여 생성되는 개체의 속성을 식별합니다. 해당 한정자는 일반적으로 선택 사항이며 생략됩니다. 다음을 작성할 수도 있습니다.
public BankAccount(string name, decimal initialBalance)
{
Owner = name;
Balance = initialBalance;
}
this
한정자는 지역 변수 또는 매개 변수의 이름이 해당 필드 또는 속성과 같은 경우에만 필요합니다. 필요한 경우가 아니면 이 문서의 나머지 부분에서 this
한정자는 생략됩니다.
생성자는 new
를 사용하여 개체를 만들 때 호출됩니다. Program.cs의 Console.WriteLine("Hello World!");
줄을 다음 코드로 바꿉니다(<name>
을 사용자의 이름으로 바꿈).
using Classes;
var account = new BankAccount("<name>", 1000);
Console.WriteLine($"Account {account.Number} was created for {account.Owner} with {account.Balance} initial balance.");
지금까지 빌드한 코드를 실행해 보겠습니다. Visual Studio를 사용하는 경우 디버그 메뉴에서 디버깅하지 않고 시작을 선택합니다. 명령줄을 사용하는 경우 프로젝트를 만든 디렉터리에 dotnet run
을 입력합니다.
계좌 번호가 공백인가요? 이를 수정해 보겠습니다. 개체가 생성될 때 계좌 번호를 지정해야 합니다. 그러나 계좌 번호 생성은 호출자의 책임이 아닙니다. BankAccount
클래스 코드는 새 계좌 번호를 지정하는 방법을 알아야 합니다. 간단한 방법은 10자리 숫자로 시작하는 것입니다. 새 계좌가 생성될 때마다 숫자가 늘어납니다. 마지막으로, 개체가 생성될 때 현재 계좌 번호를 저장합니다.
BankAccount
클래스에 멤버 선언을 추가합니다. BankAccount
클래스의 시작 부분에서 여는 중괄호 {
뒤에 다음 코드 줄을 배치합니다.
private static int s_accountNumberSeed = 1234567890;
accountNumberSeed
(은)는 데이터 멤버입니다. 이것은 private
입니다. 즉 BankAccount
클래스 내의 코드로만 액세스할 수 있습니다. 이는 전용 구현(계좌 번호가 생성되는 방법)과 공공 책임(계좌 번호를 가지는 것 등)을 구분하는 방법입니다. 또한 static
로서 모든 BankAccount
개체에서 공유됨을 의미합니다. 비정적 변수의 값은 BankAccount
개체의 각 인스턴스에 고유합니다. accountNumberSeed
(은)는 private static
필드이므로 C# 명명 규칙에 따라 s_
접두사를 가집니다. s
(은)는 static
(을)를 나타내고 _
(은)는 private
필드를 나타냅니다. 생성자에 다음 두 줄을 추가하여 계좌 번호를 지정합니다. this.Balance = initialBalance
줄 뒤에 배치합니다.
Number = s_accountNumberSeed.ToString();
s_accountNumberSeed++;
dotnet run
을 입력하여 결과를 확인합니다.
예금 및 인출 만들기
은행 계좌 클래스는 제대로 작동하려면 예금과 인출을 허용해야 합니다. 계좌의 모든 트랜잭션에 대한 저널을 만들어 예금과 인출을 구현하겠습니다. 모든 트랜잭션을 추적하는 것은 단순히 각 트랜잭션의 잔액을 업데이트하는 것보다 몇 가지 이점이 있습니다. 기록을 사용하여 모든 트랜잭션을 감사하고 일별 잔액을 관리할 수 있습니다. 필요한 경우 모든 트랜잭션의 기록에서 잔액을 계산하면 고정된 단일 트랜잭션의 오류가 다음 계산의 잔액에 올바르게 반영되도록 할 수 있습니다.
트랜잭션을 나타내는 새 형식을 생성해 보겠습니다. 트랜잭션은 책임이 없는 간단한 형식입니다. 몇 가지 속성이 필요합니다. Transaction.cs라는 새 파일을 만듭니다. 파일에 다음 코드를 추가합니다.
namespace Classes;
public class Transaction
{
public decimal Amount { get; }
public DateTime Date { get; }
public string Notes { get; }
public Transaction(decimal amount, DateTime date, string note)
{
Amount = amount;
Date = date;
Notes = note;
}
}
이제 Transaction
개체의 List<T>를 BankAccount
클래스에 추가하겠습니다. BankAccount.cs 파일의 생성자 뒤에 다음 선언을 추가합니다.
private List<Transaction> _allTransactions = new List<Transaction>();
이제 Balance
를 올바르게 계산해 보겠습니다. 모든 트랜잭션의 값을 합하여 현재 잔액을 찾을 수 있습니다. 코드가 현재 상태이기 때문에 계정의 초기 잔액만 가져올 수 있으므로 Balance
속성을 업데이트해야 합니다. BankAccount.cs의 public decimal Balance { get; }
줄을 다음 코드로 바꿉니다.
public decimal Balance
{
get
{
decimal balance = 0;
foreach (var item in _allTransactions)
{
balance += item.Amount;
}
return balance;
}
}
이 예제에서는 속성의 중요한 측면을 보여 줍니다. 이제 다른 프로그래머가 값을 요청할 때 잔액을 계산합니다. 계산은 모든 트랜잭션을 열거하고 합계를 현재 잔액으로 제공합니다.
다음으로 MakeDeposit
및 MakeWithdrawal
메서드를 구현합니다. 이러한 메서드는 마지막 두 규칙을 적용합니다. 초기 잔액은 양수여야 하며 인출은 음수 잔액을 생성하면 안 됩니다.
이러한 규칙은 예외의 개념을 소개합니다. 메서드가 작업을 성공적으로 완료할 수 없음을 나타내는 표준 방법은 예외를 throw하는 것입니다. 예외 형식 및 관련 메시지는 오류를 설명합니다. 여기서 MakeDeposit
메서드는 입금 금액이 0보다 크지 않으면 예외를 throw합니다. MakeWithdrawal
메서드는 인출 금액이 0보다 크지 않거나 인출을 적용하면 음수 잔액이 발생하는 경우 예외를 throw합니다. _allTransactions
목록의 선언 뒤에 다음 코드를 추가합니다.
public void MakeDeposit(decimal amount, DateTime date, string note)
{
if (amount <= 0)
{
throw new ArgumentOutOfRangeException(nameof(amount), "Amount of deposit must be positive");
}
var deposit = new Transaction(amount, date, note);
_allTransactions.Add(deposit);
}
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 < 0)
{
throw new InvalidOperationException("Not sufficient funds for this withdrawal");
}
var withdrawal = new Transaction(-amount, date, note);
_allTransactions.Add(withdrawal);
}
throw
문은 예외를 throw합니다. 현재 블록의 실행이 종료되고 제어가 호출 스택에 있는 처음 일치하는 catch
블록으로 전달됩니다. catch
블록을 추가하여 나중에 이 코드를 테스트합니다.
생성자는 잔액을 직접 업데이트하지 않고 초기 트랜잭션을 추가하도록 변경해야 합니다. MakeDeposit
메서드를 이미 작성했으므로 생성자에서 호출합니다. 완성된 생성자는 다음과 같아야 합니다.
public BankAccount(string name, decimal initialBalance)
{
Number = s_accountNumberSeed.ToString();
s_accountNumberSeed++;
Owner = name;
MakeDeposit(initialBalance, DateTime.Now, "Initial balance");
}
DateTime.Now은 현재 날짜 및 시간을 반환하는 속성입니다. 새 BankAccount
(을)를 만드는 코드에 따라 Main
메서드에 몇 가지 입금 및 인출을 추가하여 이 코드를 테스트합니다.
account.MakeWithdrawal(500, DateTime.Now, "Rent payment");
Console.WriteLine(account.Balance);
account.MakeDeposit(100, DateTime.Now, "Friend paid me back");
Console.WriteLine(account.Balance);
다음으로, 잔액이 음수인 계정을 생성하여 오류 조건이 발생하는지 테스트합니다. 방금 추가한 이전 코드 뒤에 다음 코드를 추가합니다.
// Test that the initial balances must be positive.
BankAccount invalidAccount;
try
{
invalidAccount = new BankAccount("invalid", -55);
}
catch (ArgumentOutOfRangeException e)
{
Console.WriteLine("Exception caught creating account with negative balance");
Console.WriteLine(e.ToString());
return;
}
try-catch
문(을)를 사용하여 예외를 throw할 수 있는 코드 블록을 표시하고 예상한 오류를 포착합니다. 동일한 기술을 사용하여 음수 잔액에 대한 예외를 throw하는 코드를 테스트할 수 있습니다. Main
메서드에서 invalidAccount
(을)를 선언하기 전에 다음 코드를 추가합니다.
// Test for a negative balance.
try
{
account.MakeWithdrawal(750, DateTime.Now, "Attempt to overdraw");
}
catch (InvalidOperationException e)
{
Console.WriteLine("Exception caught trying to overdraw");
Console.WriteLine(e.ToString());
}
파일을 저장하고 dotnet run
을 입력하여 시도해 보세요.
과제 - 모든 트랜잭션 기록
이 자습서를 완료하기 위해 트랜잭션 기록에 대해 string
을 생성하는 GetAccountHistory
메서드를 작성할 수 있습니다. BankAccount
형식에 이 메서드를 추가합니다.
public string GetAccountHistory()
{
var report = new System.Text.StringBuilder();
decimal balance = 0;
report.AppendLine("Date\t\tAmount\tBalance\tNote");
foreach (var item in _allTransactions)
{
balance += item.Amount;
report.AppendLine($"{item.Date.ToShortDateString()}\t{item.Amount}\t{balance}\t{item.Notes}");
}
return report.ToString();
}
기록은 StringBuilder 클래스를 사용하여 각 트랜잭션에 대해 한 줄이 포함된 문자열의 형식을 지정합니다. 이러한 자습서의 앞부분에서 문자열 형식 지정 코드를 살펴보았습니다. 새 문자는 \t
입니다. 이 새 문자는 탭을 삽입하여 출력 형식을 지정합니다.
Program.cs에서 테스트하려면 이 줄을 추가합니다.
Console.WriteLine(account.GetAccountHistory());
프로그램을 실행하여 결과를 확인합니다.
다음 단계
잘 알 수 없는 경우 GitHub 리포지토리에서 이 자습서의 소스를 확인할 수 있습니다.
개체 지향 프로그래밍 자습서를 계속 진행할 수 있습니다.
다음 문서에서 이러한 개념을 더 자세히 알아볼 수 있습니다.
.NET