오늘 배운 내용
-
프로그래밍에 대한 생각 정리
-
참조 타입의 복사 (얕은 복사 깊은 복사)
프로그래밍에 대한 생각 정리 - 프로그래밍은 코드만 작성하는 작업이 아니다.
오늘 튜터님의 팀 과제 해설 강의를 듣다보니 문득 그런 생각이 들었다.
변수, 반복문, 메서드, 클래스, 상속, 인터페이스 이런 모든 기능들이 결국 전부 같은 결을 가지고 있지 않나? 라는 생각
그래서 결론은 이거다 프로그래밍은 코드를 작성하는 작업이 아니라 코드의 중복을 없애는 작업이다.
사실 프로그램 자체는 같은 코드를 계속해서 작성하면 어떤 기능이든 구현할 수 있지 않나 라는 생각이 들었고,
결국 계속해서 배우던 기능 ,내용들은 전부 같은 코드를 중복해서 쓰지않고 재활용 시키는 작업, 기능이나 객체등의
공통점을 찾아서 재활용하는 작업등 코드에서 중복되는 지점들을 찾아서 묶어주어 작업 효율을 향상시키는 과정 이라고 생각됬다.
예를들어 변수의경우에도 사실 변수대신에 그냥 리터럴 값을 사용해도 프로그램을 만들수 있다.
그런데 코드를 작성할 때 일일히 한줄한줄 모든 리터럴값을 넣어줘야되고, 값을 수정해야 할 필요가 생긴다면?
모든 코드에 존재하는 해당 부분의 리터럴 값을 바꾸어 주어야된다.
그런데 만약 이 리터럴 값을 어떠한 변수로 선언해서 관리한다면?
리터럴 값은 변수에 한번 입력해주고, 그 뒤로는 변수를 사용하면 같은 값을 계속해서 사용할 수 있다.
뭐 좀 극단적으로 예를 들어 “HelloWorld!HelloWorld!HelloWorld!” 라는 문자열 리터럴을 굉장히 많이 출력하는
코드를 작성한다고 생각하면 만약 변수를 사용하지 않는다면 ?
Console.WriteLine("HelloWorld!HelloWorld!HelloWorld!");
Console.WriteLine("HelloWorld!HelloWorld!HelloWorld!");
Console.WriteLine("HelloWorld!HelloWorld!HelloWorld!");
Console.WriteLine("HelloWorld!HelloWorld!HelloWorld!");
Console.WriteLine("HelloWorld!HelloWorld!HelloWorld!");
Console.WriteLine("HelloWorld!HelloWorld!HelloWorld!");
Console.WriteLine("HelloWorld!HelloWorld!HelloWorld!");
Console.WriteLine("HelloWorld!HelloWorld!HelloWorld!");
뭐 이렇게 작성해야 될것이다. 그런데 여기서 갑자기 “HelloWorld!HelloWorld!HelloWorld!” 가아니라
“HelloWorld!” 만 출력하고 싶으면 코드를 아래처럼 한줄 한줄 전부 수정해야한다.
Console.WriteLine("HelloWorld!");
Console.WriteLine("HelloWorld!");
Console.WriteLine("HelloWorld!");
Console.WriteLine("HelloWorld!");
Console.WriteLine("HelloWorld!");
Console.WriteLine("HelloWorld!");
Console.WriteLine("HelloWorld!");
Console.WriteLine("HelloWorld!");
그런데 변수를 사용한다면? 긴 문자열 리터럴을 한번만 입력해도 변수를 통해서 코드를 작성할 수 있고
string str = "HelloWorld!HelloWorld!HelloWorld!"
Console.WriteLine(str);
Console.WriteLine(str);
Console.WriteLine(str);
Console.WriteLine(str);
Console.WriteLine(str);
Console.WriteLine(str);
그리고 출력 값을 바꾸고 싶다면?
변수에 할당된 문자열 리터럴만 수정하면 변수를 사용한 모든곳에 있는 값이 바뀔 것 이다.
string str = "HelloWorld!"
Console.WriteLine(str);
Console.WriteLine(str);
Console.WriteLine(str);
Console.WriteLine(str);
Console.WriteLine(str);
Console.WriteLine(str);
그리고 여기서 반복문을 쓴다면? 코드가 더 줄어들게된다.
string str = "HelloWorld!";
for(int i =0 ; i < 7 ; i++>)
{
Console.WriteLine(str);
}
이렇게 변수, 반복문을 사용하면서 점점 코드의 중복되는 부분이 사라졌고 수정하기도 쉬워진다.
그리고 이런 반복문을 이곳 저곳에서 사용해야된다면 또 반복되는 부분이 생길 것이다.
string str = "HelloWorld!";
for(int i =0 ; i < 7 ; i++>)
{
Console.WriteLine(str);
}
for(int i =0 ; i < 7 ; i++>)
{
Console.WriteLine(str);
}
for(int i =0 ; i < 7 ; i++>)
{
Console.WriteLine(str);
}
하지만 메서드를 사용해서 이 기능을 구현했다면?
이런식으로 또 중복을 줄일 수 있고 매서드에 넣는 매개변수에 따라서 출력값을 바꿀수 도있다
public void WriteRepeat(string str)
{
for(int i =0 ; i < 7 ; i++>)
{
Console.WriteLine(str);
}
}
string str = "HelloWorld!";
string str2 = "HelloWorld2!";
WriteRepeat(str);
WriteRepeat(str2);
WriteRepeat(str);
뭐 이후로도 똑같다. 변수, 반복문, 메서드, 클래스, 상속등 전부다 결국에는 없더라도 프로그램을 문제없이 돌아가게 할 수 있지만.
코드를 작성할 때 많은 시간이들고 코드를 수정할때도 많은 시간이 소모 된다. 그리고 중복된 부분이 너무많아서 제대로 코드를 알아 보기 힘들것 이다.
그래서 결론은 우리가 여러가지를 고려하면서 프로그램의 구조를 생각하고 코드를 작성하는 모든 프로그래밍 과정은
코드를 작성하기 보단 코드들을 여러가지 방법으로 묶어서 중복을 없애 최적화를 하는 작업 이라고 생각한다.
참조 타입의 복사 (얕은 복사 깊은 복사)
어제 저장 불러오기 기능을 적용하면서 문제가 발생했었다 그 이유는
Scene 클래스에서 DugeonGame.Instance.player를 바로 사용하지않고
Scene클래스의 _player 변수가 DugeonGame.Instance.player를 참조하여서 사용했기 때문이였다,
팀원 분들과 이야기를 나누면서 결국에는 Scene클래스에서 DungeonGame.Instance.player를 참조하지 않고
DungeonGame.Instance.player 그 자체를 사용하는게 객체를 사용할 때 문제가 발생하지 않고 깔끔했을 것 같다는 결론을 냈다.
이와 관련된 이야기가 얕은 복사, 깊은 복사 이다.
튜터님이 팀 과제를 해설해 주시면서 깊은 복사를 사용하는 예시를 보여 주셨는데, 어떤 차이가 있었나 하면
우리는 _player 변수가 DungeonGame.Instance.player를 참조 , 즉 얕은 복사 하고 있었기 때문에
만약 (사실 만약이 아니라 객체의 정보를 불러와서 할당하면서 실제로 발생했다.) DungeonGame.Instance.player에
새로운 Player객체가 할당 됬다면 Scene._player 변수와 DungonGame.Instance.player는 서로 다른 객체를 가르키게 된다.
내 이해는 이렇다 사실 우리는 객체를 선언할때
Player _player = new Player();
이런 식으로 선언하게 되는데 내가 이해하는 바는 사실 _player는 객체가 아니라 참조 변수이다.
그냥 스택에 선언된 Player 타입을 가르키는 참조 변수 C나 C++로 치면 포인터 변수의 개념이라고 생각하면 좋다.
참조 변수와 포인터는 비슷한 역할을 하지만 차이점이 존재한다.
그리고 진짜 객체는 힙에 있는 new Player(); 다
DungeonGame.Instance.player = new Player();
Player _player = DungeonGame.Instance.player;
그럼 이렇다면 _player는 뭘 가르키는가? 앞서 말한대로 _player는 Player타입 참조 변수다
DungeonGame.Instance.player 도 사실 Player 타입 참조 변수다.
그리고 그뒤에 있는 new Player()가 실제로 힙에 존재하는 Player객체이다.
이러면 결국에는 _player와 Dungeon.Instance.player 모두 한 Player 객체이고
_player는 Dungeon.Instance.player를 얕은 복사
한것이다.
그리고 난 이걸 복사가 아니라 참조
라고 말해야된다고 생각한다.
이 상황에서 Json으로 저장된 Player의 정보를 불러와서 우리 게임의 유일한 Player인
DungeonGame.Instance.player에 객체를 저장한다. 그러면 어떻게 될까?
우리는 Json으로 새로운 Player객체를 생성해서 DungeonGame.Instance.player에 저장하게된다.
새로운 객체를 생성
해서 재할당
하는 것이다.
사실 이 과정이 깊은 복사는 아니지만 결론적으로는 깊은 복사의 개념과 같다고 생각한다.
왜나면 깊은 복사도 새로운 객체를 생성해서 그 안에 데이터를 대상과 똑같게 만들어주는 거니까
깊은 복사가 무엇이냐? 우리가 객체를 평범하게 = 로 배정하게 되면 이건 사실
얕은 복사라고 말하지만 복사가 아니라 참조의 형태다 그렇다면 객체를 복사라려면 어떻게해야할까?
답은 객체의 정보를 그대로 복사해서 새로운 객체를 생성한다. 그러면 똑같은 정보를 담은 객체가 2개가 되는거다.
결국 깊은 복사
는 같은 정보를 가진 객체를 새로 생성
하는 것이다.
그래서 우리가 DungeonGame.Instance.player에 새로운 객체를 생성하게 되면
_player와 DungeonGame.Instance.player는 전혀 다른 객체를 가르키게 된다.
우리는 이때 Scene이 생성될때 DungeonGame.Instance.player를 참조하도록 코드를 짰고,
게임 도중 불러오기 상황이 발생하면 우리가 불러온 데이터는 DungeonGame.Instance.player가 가르키고
불러오기 전 원래의 데이터는 Scene의 _player가 가르키게되서 제대로 데이터를 불러올 수 없다.
우리가 해결한 방법은 사실 매 Scene을 로드할 때 마다 계속해서 _player가 DungeonGame.Instance.player를 참조하는 방법 이다.
이러면 로드를 했더라도 _player를 사용하기 직전에 다시 _player가 DungeonGame.Instance.player를 가르키기 때문에
얕은 복사로도 불러온 데이터를 참조 할 수 있었다.
결론
얕은 복사는 참조타입을 참조.
깊은 복사는 참조타입을 새로 할당해서 데이터를 덮어쓴다.