Dart 응용 : 사전 만들기

윤뿔소·2023년 5월 5일
0

Dart / Flutter

목록 보기
6/18
post-thumbnail

노마드코더 챌린지를 하는 도중 과제가 있었다. 조건은 아래와 같다.

과제 조건

Challenge goals:

Using everything we learned, make a Dictionary class with the following methods:

  • add: 단어를 추가함.
  • get: 단어의 정의를 리턴함.
  • delete: 단어를 삭제함.
  • update: 단어를 업데이트 함.
  • showAll: 사전 단어를 모두 보여줌.
  • count: 사전 단어들의 총 갯수를 리턴함.
  • upsert 단어를 업데이트 함. 존재하지 않을시. 이를 추가함. (update + insert = upsert)
  • exists: 해당 단어가 사전에 존재하는지 여부를 알려줌.
  • bulkAdd: 다음과 같은 방식으로. 여러개의 단어를 한번에 추가할 수 있게 해줌. [{"term":"김치", "definition":"대박이네~"}, {"term":"아파트", "definition":"비싸네~"}]
  • bulkDelete: 다음과 같은 방식으로. 여러개의 단어를 한번에 삭제할 수 있게 해줌. ["김치", "아파트"]

Requirements:

  • Use class
  • Use typedefs
  • Use List
  • Use Map

위의 조건을 충족하면서 클래스 Dictionary를 만들어야한다. 즉 추가, 검색, 수정, 삭제, 총 단어 수, 등의 작업을 수행할 수 있는 클래스를 만들자!

구현

typedef 사용

타입 별칭을 사용할 수 있는 typedef를 통해 재사용성을 높여보자!
조건을 봤을 때 많이 쓰일 거 같은 자료구조가 Map<String, String>이다.

왜냐하면 클래스 기본 속성으로 선언할 _dictionary가 리스트 안에 들어가는 Map<String, String>구조이기도 하고, bulk~ 메소드들을 봤을 때 리스트[] 안에 Map<String, String>이기 때문에 typedef을 써서 재사용성을 높여보자!

// typedef 사용
typedef DictionaryData = Map<String, String>;

추상 클래스(Abstract Class) 정의

사실 추상은 필수가 아닌데, 일단 OOP의 꽃은 빼놓을 수가 없으니 먼저 정의해보자.

  1. 추상 클래스를 사용하여 공통된 메서드와 구조를 정의함
  2. Named Parameter를 사용하여 가독성을 높임
  3. 추상 클래스는 구현을 가지지 않기 때문에 구체적인 구현은 자식 클래스에서 이루어질 예정

위와 같이 계획을 잡았다. 메소드는 위에 조건을 보며 하나씩 대조해가며 만들었다. 또 Named를 많이 쓴다고들 하니 일단 마스터하려고 Named Parameter로 만들었다.

// Abstract class 정의
abstract class AbstractDictionary {
  // Named parameter 사용
  void add({required String term, required String definition});
  String get({required String term});
  void delete({required String term});
  void update({required String term, required String definition});
  void showAll();
  int count();
  void upsert({required String term, required String definition});
  bool exists({required String term});
  void bulkAdd(List<DictionaryData> terms);
  void bulkDelete(List<String> terms);
}

void, String, int 등 반환 타입에 따라 작성했고, 뭐가 들어올지에 따라서도 만들었다.
특히 bulk~ 메소드를 만들면서 조금 구글링의 힘을 빌리긴 했는데 간단하게 만들었다. 별칭 선언한 DictionaryData도 사용했다!

받는 객체를 terms로 지정 후 나중에 메소드 구현할 때 쓸 것이다!

자식 클래스(Dictionary) 구현

가장 핵심이자 꽃인 Dictionary 클래스를 구현해보자!

  • _dictionary 필드를 private으로 선언하여 캡슐화(encapsulation)를 구현함
    • 변수 앞에 언더바를 붙이면 클래스 내에서만 사용하는 변수라는 걸 명시한다!
  • add, get, delete, update, showAll, count, upsert, exists, bulkAdd, bulkDelete 메서드를 통해 사전 작업을 수행할 수 있음
    • 조건문 : get, update는 조건을 두어 단어의 존재를 분기 처리
    • 반복문 : showAll, bulkAdd, bulkDeletefor를 사용.(for)
    • containsKey : exists, update 특정 키가 존재하는지의 대한 유무를 불린으로 확인
  • @override를 작성해 추상클래스의 부모에게 빌려왔다는 걸 명시
// Dictionary 클래스 구현
class Dictionary implements AbstractDictionary {
  // 필드 정의
  final DictionaryData _dictionary = {};

  
  void add({required String term, required String definition}) {
    _dictionary[term] = definition;
  }

  
  String get({required String term}) {
    return _dictionary[term] ?? '해당 단어가 사전에 없습니다.';
  }

  
  void delete({required String term}) {
    _dictionary.remove(term);
  }

  
  void update({required String term, required String definition}) {
    if (_dictionary.containsKey(term)) {
      _dictionary[term] = definition;
    } else {
      print('해당 단어가 사전에 없습니다.');
    }
  }

  
  void showAll() {
    _dictionary.forEach((term, definition) {
      print('$term : $definition');
    });
    // for (var term in _dictionary.keys) {
    //   print('$term : ${_dictionary[term]}');
    // }
  }

  
  int count() {
    return _dictionary.length;
  }

  
  void upsert({required String term, required String definition}) {
    _dictionary[term] = definition;
  }

  
  bool exists({required String term}) {
    return _dictionary.containsKey(term);
  }

  
  void bulkAdd(List<DictionaryData> terms) {
    for (var term in terms) {
      _dictionary[term['term']!] = term['definition']!;
    }
  }

  
  void bulkDelete(List<String> terms) {
    for (var term in terms) {
      _dictionary.remove(term);
    }
  }
}

끝! 재밌다! containsKey??같이 다트에만 있는 기능을 어떻게든 넣으려고 발악했다 ㅋㅋㅋ

main 함수 작성

가장 간단하다.

그냥 Dictionary 클래스의 인스턴스를 생성한 후, 위에서 정의한 메소드들을 이용하여 사전에 단어를 추가하거나 수정하거나 삭제하거나 조회하는 등의 동작을 수행하기만 하면 된다.

void main() {
  var dictionary = Dictionary();

  // 단어 추가
  dictionary.add(term: 'apple', definition: '사과나무의 열매');

  // 단어 가져오기
  print(dictionary.get(term: 'apple')); // 출력: 사과나무의 열매

  // 없는 단어 가져오기
  print(dictionary.get(term: 'banana')); // 출력: 해당 단어가 사전에 없습니다.

  // 단어 수정
  dictionary.update(
      term: 'apple', definition: '사과나무의 열매로, 세계적으로 널리 재배되는 열매 가운데 하나이다.');

  print(dictionary.get(
      term: 'apple')); // 출력: apple: 사과나무의 열매로, 세계적으로 널리 재배되는 열매 가운데 하나이다.

  // 단어 추가 또는 수정
  dictionary.upsert(term: 'banana', definition: '바나나');

  // 단어 삭제
  dictionary.delete(term: 'banana');

  // 단어 존재 여부 확인
  print(dictionary.exists(term: 'apple')); // 출력: true
  print(dictionary.exists(term: 'banana')); // 출력: false

  // 대량 단어 추가
  List<DictionaryData> bulkTerms = [
    {'term': 'cherry', 'definition': '체리'},
    {'term': 'orange', 'definition': '오렌지'}
  ];
  dictionary.bulkAdd(bulkTerms);

  dictionary.showAll();
  // apple: 사과나무의 열매로, 세계적으로 널리 재배되는 열매 가운데 하나이다.
  // cherry: 체리
  // orange: 오렌지

  // 단어 개수 세기
  print(dictionary.count()); // 출력: 3

  // 대량 단어 삭제
  List<String> bulkDeleteTerms = ['cherry', 'orange'];
  dictionary.bulkDelete(bulkDeleteTerms);

  dictionary.showAll(); // apple: 사과나무의 열매로, 세계적으로 널리 재배되는 열매 가운데 하나이다.
}

모든 메소드들을 빠짐없이 사용해봤다. 주석도 처리해놨으니 흐름만 타고가도 알 것이다!

결과

// typedef 사용
typedef DictionaryData = Map<String, String>;

// Abstract class 정의
abstract class AbstractDictionary {
  // Named parameter 사용
  void add({required String term, required String definition});
  String get({required String term});
  void delete({required String term});
  void update({required String term, required String definition});
  void showAll();
  int count();
  void upsert({required String term, required String definition});
  bool exists({required String term});
  void bulkAdd(List<DictionaryData> terms);
  void bulkDelete(List<String> terms);
}

// Dictionary 클래스 구현
class Dictionary implements AbstractDictionary {
  // 필드 정의
  final DictionaryData _dictionary = {};

  
  void add({required String term, required String definition}) {
    _dictionary[term] = definition;
  }

  
  String get({required String term}) {
    return _dictionary[term] ?? '해당 단어가 사전에 없습니다.';
  }

  
  void delete({required String term}) {
    _dictionary.remove(term);
  }

  
  void update({required String term, required String definition}) {
    if (_dictionary.containsKey(term)) {
      _dictionary[term] = definition;
    } else {
      print('해당 단어가 사전에 없습니다.');
    }
  }

  
  void showAll() {
    _dictionary.forEach((term, definition) {
      print('$term : $definition');
    });
    // for (var term in _dictionary.keys) {
    //   print('$term : ${_dictionary[term]}');
    // }
  }

  
  int count() {
    return _dictionary.length;
  }

  
  void upsert({required String term, required String definition}) {
    _dictionary[term] = definition;
  }

  
  bool exists({required String term}) {
    return _dictionary.containsKey(term);
  }

  
  void bulkAdd(List<DictionaryData> terms) {
    for (var term in terms) {
      _dictionary[term['term']!] = term['definition']!;
    }
  }

  
  void bulkDelete(List<String> terms) {
    for (var term in terms) {
      _dictionary.remove(term);
    }
  }
}

void main() {
  var dictionary = Dictionary();

  // 단어 추가
  dictionary.add(term: 'apple', definition: '사과나무의 열매');

  // 단어 가져오기
  print(dictionary.get(term: 'apple')); // 출력: 사과나무의 열매

  // 없는 단어 가져오기
  print(dictionary.get(term: 'banana')); // 출력: 해당 단어가 사전에 없습니다.

  // 단어 수정
  dictionary.update(
      term: 'apple', definition: '사과나무의 열매로, 세계적으로 널리 재배되는 열매 가운데 하나이다.');

  print(dictionary.get(
      term: 'apple')); // 출력: apple: 사과나무의 열매로, 세계적으로 널리 재배되는 열매 가운데 하나이다.

  // 단어 추가 또는 수정
  dictionary.upsert(term: 'banana', definition: '바나나');

  // 단어 삭제
  dictionary.delete(term: 'banana');

  // 단어 존재 여부 확인
  print(dictionary.exists(term: 'apple')); // 출력: true
  print(dictionary.exists(term: 'banana')); // 출력: false

  // 대량 단어 추가
  List<DictionaryData> bulkTerms = [
    {'term': 'cherry', 'definition': '체리'},
    {'term': 'orange', 'definition': '오렌지'}
  ];
  dictionary.bulkAdd(bulkTerms);

  dictionary.showAll();
  // apple: 사과나무의 열매로, 세계적으로 널리 재배되는 열매 가운데 하나이다.
  // cherry: 체리
  // orange: 오렌지

  // 단어 개수 세기
  print(dictionary.count()); // 출력: 3

  // 대량 단어 삭제
  List<String> bulkDeleteTerms = ['cherry', 'orange'];
  dictionary.bulkDelete(bulkDeleteTerms);

  dictionary.showAll(); // apple: 사과나무의 열매로, 세계적으로 널리 재배되는 열매 가운데 하나이다.
}
  • 코드를 실행하여 Dictionary 작업을 수행할 수 있음
  • 공통적인 기능은 추상 클래스에 정의되어 있으므로, 다른 자식 클래스에서도 이를 활용할 수 있음
  • 코드를 수정하여 다양한 기능을 추가할 수 있음

추상 클래스와 자식 클래스의 구분을 통해 추상화와 캡슐화를 구현하고, 맵(Map) 객체를 사용해서 단어와 정의를 저장했다.

이를 통해 다양한 기능을 더 만들어내거나 상속, Mixins 할 수 있고, main 함수에서 dictionary 인스턴스를 조작할 수도 있다.

아쉬운 건 Named Constructor도 실습을 해보고 싶었는데 그거까지 하기에는 좀 오바인 거 같아서 패스! 다음엔 진짜 플러터다!

profile
코뿔소처럼 저돌적으로

1개의 댓글

comment-user-thumbnail
2023년 6월 30일

안녕하세요. 혹시 bulkAdd 메소드에서 사용하신 문법이 뭔지 알 수 있을까요?

답글 달기