public class Example
{
public static string SwitchChar(string input)
{
char[] sArray = input.ToCharArray();
for (int i = 0; i < sArray.Length; i++)
{
if (char.IsUpper(sArray[i]))
{
sArray[i] = char.ToLower(sArray[i]);
}
else if (char.IsLower(sArray[i]))
{
sArray[i] = char.ToUpper(sArray[i]);
}
}
return new string(sArray);
}
public static void Main()
{
String s;
Console.Clear();
s = Console.ReadLine();
s = SwitchChar(s);
Console.Write(s);
}
}
2. 특수문자 출력해보기
!@#$%^&*(\'"<>?:;
이걸 출력해야 한다 ㅋㅋㅋㅋ
using System;
using System.Collections.Generic;
public class Example
{
public static void Main()
{
List<int> asciis = new List<int> {33,64,35,36,37,94,38,42,40,92,39,34,60,62,63,58,59};
foreach (int c in asciis)
{
Console.Write((char) (c));
}
}
}
정예형 몬스터일 경우 사용되는 패턴이나 기술 등은 유사하지만 능력치가 좀 더 강화된 특징을 갖는다면
새로운 몬스터 클래스를 만들필요없이기본 객체를 복사해서 사용하는 방식이다!
- 프로토타입(Prototype)의 특징
기존 객체를 기본값으로 활용하기에 매번 DB에서 불러오지 않아도 되서 개발 비용을 줄일 수 있다!
단 객체 간의 의존성이 클경우 관리하기 어려울 수 있다.
- 프로토타입(Prototype) 예제 만들어보자! (C#)
1. 먼저 몬스터 프로토타입 인터페이스를 정의한다.
// 몬스터 프로토타입
public abstract class MonsterPrototype
{
public string Name { get; set; }
public int MaxHealth { get; set; }
public int CurHealth { get; set; }
public int Strength { get; set; }
public abstract MonsterPrototype Clone();
}
2. 몬스터 클래스를 만든다.
public class Monster : MonsterPrototype
{
public Monster(string name, int health, int strength)
{
Name = name;
MaxHealth = health;
CurHealth = MaxHealth;
Strength = strength;
}
public override MonsterPrototype Clone()
{
return (Monster)this.MemberwiseClone();
}
public void Show() {
Console.WriteLine($"[몬스터] {Name} - 체력: {CurHealth}, 공격력: {Strength}");
}
}
3. Main에서 몬스터 프로토타입과 정예형 몬스터를 만든다.
internal class Program
{
static void Main(string[] args)
{
// 기본 몬스터 원형
Monster goblin = new Monster("고블린", 100, 15);
goblin.Show();
// 고블린 정예폼
Monster goblin_Elite = (Monster)goblin.Clone();
goblin_Elite.Name = "정예 고블린"; // 이름 변경 가능
goblin_Elite.Strength += 10; // 공격력 증가
goblin_Elite.Show();
}
}
이 부분이 프로토타입 패턴을 사용한 예시이다!
Monster goblin_Elite = (Monster)goblin.Clone();
기존 고블린 객체를 복제해서 새로운 정예 고블린 객체(goblin_Elite)를 만들었다!
체력, 방어, 속도, 마나게이지 뿐만 아니라 가지고 있는 기술, 아직 해금되지 않는 기술들도
전부 캐릭터가 가진 속성이다. 이 캐릭터를 생성자로 만든다고 생각해보자.
- 생성자로 캐릭터를 만들 경우
new 캐릭터1("전사", "근접", 100, 50, 20, 150, ...);
이런식으로 생성할텐데, 일단 생성자에 너무 많은 매개변수가 포함되고, 속성의 순서를 기억하기가 어렵다!
이 코드를 만든 당사자면 이해할 순 있겠지만 프로그래밍에선 협업이 중요하다!
다른 사람이 내 코드를 봤을때 이 코드가 무엇인지 확실히 알아야 되는데 저 생성자 코드만 보고
캐릭터를 만들 때 무슨 속성을 설정할지 단번에 파악할 수 있을까?
- Builder 패턴을 적용할경우
Character warrior = new CharacterBuilder()
.SetName("전사")
.SetType("근접")
.SetHealth(100)
.SetDefense(50)
.SetSpeed(20)
.SetMana(150)
.Build();
이 방법은 속성의 순서를 기억할 필요가 없고, 해당 속성이 무엇인지 명확해 보인다!
- Builder란 무엇인가?
복잡한 인스턴스를 조립하는 방식!
객체를 생성하는 방법(How)과 객체를 구현하는 방법(What)을 분리하여, 동일한 생성 절차에서 다양한 표현 결과를 만들 수 있도록 하는 패턴이다.
복잡한 객체를 생성과 구현을 분리해서 관리해주는 느낌으로 생각하면 된다!
-Builder 패턴을 사용해보자! (c#)
1. 먼저 캐릭터 클래스를 설계한다!
캐릭터는 이름, 직업, 무기, 체력, 마나, 속도 속성이 있다!
public class PlayerCharacter
{
public string Name { get; set; }
public string Job { get; set; }
public string Weapon { get; set; }
public int Health { get; set; }
public int Mana { get; set; }
public int Speed { get; set; }
public void PlayerShow()
{
Console.WriteLine($"[캐릭터 생성 완료] 이름: {Name}, 직업: {Job}, 무기: {Weapon}, " +
$"체력: {Health}, 마나: {Mana}, 속도: {Speed}");
}
}
자동차 인터페이스는 자동차가 반드시 있어야 하는 기능(시동 on / off, 이동기능들)들을 정의하는 것!
- 코드로 보는 클래스의 특징 (c#)
예시 코드는 모든 게임 유닛의 Base가 되는 BaseUnit 클래스가 있고
이 BaseUnit을 상속받아서 각각 플레이어 캐릭터와 적 캐릭터를 만드는 간단한 예시이다.
상황은 간단하다. 플레이어랑 적 캐릭터가 서로를 향해 공격을 한다!
// 기본적인 유닛 클래스
class BaseUnit
{
public string Name { get; set; }
public int maxHp { get; set; }
public int curHp;
public BaseUnit(string name, int health)
{
Name = name;
maxHp = health;
curHp = maxHp;
}
public virtual void TakeAction(BaseUnit target)
{
Console.WriteLine($"{Name}의 행동");
}
public void TakeDamage(int damage)
{
curHp -= damage;
Console.WriteLine($"{Name}가 {damage}의 피해를 입었다. {Name}의 체력: {curHp}\n");
}
}
// 플레이어 클래스 (BaseUnit 상속)
class Player : BaseUnit
{
public Player(string name, int health) : base(name, health) { }
public override void TakeAction(BaseUnit target)
{
Attack(target);
}
public void Attack(BaseUnit target)
{
Console.WriteLine($"{Name}가 {target.Name}을 공격했다!");
target.TakeDamage(10);
}
}
// 적 캐릭터 클래스 (BaseUnit 상속)
class Enemy : BaseUnit
{
public Enemy(string name, int health) : base(name, health) { }
public override void TakeAction(BaseUnit target)
{
Console.WriteLine($"{Name}의 공격! {target.Name}는 피해를 입었다.");
target.TakeDamage(5);
}
}
internal class Program
{
static void Main(string[] args)
{
Player player = new Player("주인공", 100);
Enemy enemy = new Enemy("고블린", 50);
player.TakeAction(enemy);
enemy.TakeAction(player);
}
}
클래스는 속성과 메서드를 만들 수 있다.
사용할 때는 객체를 생성해서 사용하며, 상속을 통해서 클래스를 확장시킬 수 있다.
- 코드로 보는인터페이스의 특징
이번에는 피해를 받는 인터페이스를 정의하고, 피해 인터페이스를 BaseUnit이 상속받았다.
상황은 이번에도 똑같이 서로를 향해 공격을 가하는데 적 캐릭터가 가하는 공격은 방패가 피해를 받는다!
namespace Class_Interface
{
// 공격을 받는 인터페이스 정의
interface IAttackable
{
void TakeDamage(int damage);
}
// 기본적인 유닛 클래스
class BaseUnit : IAttackable
{
public string Name { get; set; }
public int maxHp { get; set; }
public int curHp;
public BaseUnit(string name, int health)
{
Name = name;
maxHp = health;
curHp = maxHp;
}
public virtual void TakeAction(IAttackable target)
{
Console.WriteLine($"{Name}의 행동");
}
public void TakeDamage(int damage)
{
curHp -= damage;
Console.WriteLine($"{Name}가 {damage}의 피해를 입었다. {Name}의 체력: {curHp}\n");
}
}
// 플레이어 클래스 (BaseUnit 상속)
class Player : BaseUnit
{
public Player(string name, int health) : base(name, health) { }
public override void TakeAction(IAttackable target)
{
Attack(target);
}
public void Attack(IAttackable target)
{
Console.WriteLine($"{Name}의 공격!");
target.TakeDamage(10);
}
}
// 적 캐릭터 클래스 (BaseUnit 상속)
class Enemy : BaseUnit
{
public Enemy(string name, int health) : base(name, health) { }
public override void TakeAction(IAttackable target)
{
Console.WriteLine($"{Name}의 공격!");
target.TakeDamage(5);
}
}
// 방어구 클래스 (IAttackable 인터페이스 구현)
class Shield : IAttackable
{
public int Durability { get; set; }
public Shield(int durability)
{
Durability = durability;
}
public void TakeDamage(int damage)
{
Durability -= damage;
Console.WriteLine($"방패가 {damage}의 피해를 흡수했습니다! 남은 내구도: {Durability}");
}
}
internal class Program
{
static void Main(string[] args)
{
Player player = new Player("주인공", 100);
Enemy enemy = new Enemy("고블린", 50);
Shield shield = new Shield(30);
player.TakeAction(enemy);
enemy.TakeAction(shield);
}
}
}
인터페이스의 실행 결과
클래스와 다른점은 인터페이스는 공격 선언만 하고, 내용은 없다.
하지만 클래스가 인터페이스를 구현 (implement) 한다면 이 공격 기능은 반드시 구현시켜야 한다!
상속(Extends)과 구현(Implements)의 차이점만 이해한다면 좀 더 알기 쉬워진다
클래스 상속
인터페이스 구현
관계
부모-자식 관계 (is-a 관계)
능력을 받는다 (Can-do 관계)
개수
단일 상속만 가능 (1개)
다중 구현이 가능 (여러개)
내용
부모의 속성/메서드를 물려받음
메서드 선언만 약속하고 구현은 하위에서 담당!
목적
코드 재사용성 (확장성)
공통된 행위 약속
아주 간단하게 정리하자면
클래스는 "나는 누구?이다." (플레이어, 적 개체를 생성한다)
인터페이스는 "나는 이 무언가?를 할 수 있다." ('공격을 받을 수 있다' 정의를 설정한다)
// 초원 스테이지 팩토리
class StageGrass_Factory : IStageFactory
{
public IMonster CreateMonster() => new Slime();
}
// 동굴 스테이지 팩토리
class StageCave_Factory : IStageFactory
{
private Random random = new Random();
public IMonster CreateMonster()
{
if (random.Next(2) == 0)
return new Goblin();
else
return new Troll();
}
}
5. 만든 예시코드로 게임을 실행해본다
// 게임 매니저
class GameManager
{
private IMonster monster;
public GameManager(IStageFactory factory)
{
monster = factory.CreateMonster();
}
public void Play()
{
monster.Attack();
}
}
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("초원 스테이지 시작!");
GameManager grassGame = new GameManager(new StageGrass_Factory());
grassGame.Play();
Console.WriteLine("\n동굴 스테이지 시작!");
GameManager caveGame = new GameManager(new StageCave_Factory());
caveGame.Play();
}
}
실행결과
추상 팩토리 패턴은 연관된 객체(몬스터 + 아이템 등)를 세트로 생성할 때 사용되는
디자인 패턴이다. 게임에서 스테이지, 종족, 장비 등의 세트를 한 번에 생성해야 할 때 사용된다.