Flutter extends vs implements vs with

|

extends

extends는 상속을 위해 사용하는 키워드입니다. Dart 언어에서 상속은 오직 하나의 부모만 가질 수 있습니다. 우리가 일반적으로 생각하고 있는 상속과 똑같습니다. 심지어 문법도 Java와 거의 비슷합니다.

class Vehicle {
  final String name;
  final String type = 'Vehicle';

  Vehicle(this.name);
}

class Car extends Vehicle {
  Car(String name) : super(name);
}

class Taxi extends Car {
  Taxi(String name) : super(name);
}

void main() {
  final taxi = Taxi('카카오 택시');

  print('name: ${taxi.name}, type: ${taxi.type}');
}


오버라이딩은 아래와 같은 방법으로 할 수 있습니다.

class Taxi extends Car {
  Taxi(String name) : super(name);

  @override
  String get type => 'Taxi';
}


implements

위에서 extends는 부모 클래스의 속성, 변수, 함수까지 모두 상속받았습니다. 하지만 오로지 부모 클래스의 인터페이스만 구현하고 싶을 때는 implements를 사용하는 것이 좋습니다.

class Taxi implements Car {
  
}

위와 같이 정의하면 2개의 메소드를 구현하라는 오류 메시지가 나옵니다. 부모 클래스에서 갖고 있는 nametype에 대한 메소드를 구현해야 합니다. IntelliJ와 같은 IDE의 힘을 빌러 자동 완성을 하면 다음과 같은 코드가 만들어집니다.

class Taxi implements Car {
  @override
  String get name => throw UnimplementedError();

  @override
  String get type => throw UnimplementedError();

}


기존에 상속으로 구현한 코드와 똑같이 만들기 위해서는 다음과 같이 작성하면 됩니다.

class Taxi implements Car {
  final String name;

  Taxi(this.name);

  @override
  String get type => 'Taxi';
}

implementsextends에 비해 가지는 가장 큰 장점은 다중 구현이 가능하다는 점입니다. (상속은 오직 하나의 부모만 가질 수 있습니다.)


with

with 키워드는 용도가 조금 다릅니다. 앞서 언급한 extendsimplements의 특징을 모두 갖고 있습니다.

extends는 속성이나 메소드들도 모두 상속받기 때문에 하위 클래스에서 부모 클래스의 메소드들을 특별한 구현없이 바로 사용이 가능합니다. 대신 하나의 부모 클래스만 가질 수 있었습니다.

implements는 여러 부모 클래스를 가질 수 있었지만, 인터페이스의 구현과 마찬가지로 하위 클래스에서 모든 메소드를 오버라이딩하여 다시 구현을 해줘야 합니다.

with는 여러 개의 부모 클래스를 가질 수 있으며, 각 메소드를 일일이 구현하지 않더라도 부모에서 구현된 메소드 호출을 할 수 있습니다.

React 기반 Chrome Extension Sample

|

React 프로젝트 생성

yarn create react-app snowdeer-react-chrome-extension-sample

cd snowdeer-react-chrome-extension-sample


public/manifest.json 파일 수정

{
  "manifest_version": 2,

  "name": "Extension Sample",
  "description": "SnowDeers' Sample extension for Chrome extension",
  "version": "0.0.1",

  "browser_action": {
    "default_popup": "index.html",
    "default_title": "SnowDeer's React Chrome Extension Sample"
  },
  "icons": {
    "16": "logo192.png",
    "48": "logo192.png",
    "128": "logo192.png"
  },
  "content_security_policy": "script-src 'self' 'sha256-[여기는 별도 해시 생성을 해야 합니다]'; object-src 'self'",  
  "permissions": [
  ]
}

manifest_version 버전 2 부터는 CSP(Content Security Policy)가 추가되었습니다. 더 자세한 내용은 여기를 참고하세요.

SHA-256 값은 별도 해시 함수 등을 이용해서 생성할 수 있습니다. 만약 생성이 어렵더라도 나중에 크롬 브라우저에서 실행을 해보면 해당 스크립트에 알맞은 해시값이 포함된 오류를 볼 수 있기 때문에 그 때 코드를 획득해도 됩니다.


src/index.css 파일 수정

굳이 안해도 되는 부분이지만, 작성한 프로그램의 실행 창의 최소 크기를 지정해줬습니다.

body {
  ...
  min-width:800px;
  min-height:800px;
  ...
}


빌드

다음 명령어를 이용해서 프로젝트를 빌드합니다.

yarn build

빌드 결과물은 build 디렉토리에 생성됩니다.


크롬 브라우저로 실행

크롬 브라우저에서 다음 주소로 접속합니다.

chrome://extensions/ 

그리고 오른쪽 상단의 Developer Mode를 활성화합니다.

왼쪽 상단 부분에 Load Unpacked 버튼을 누른 다음 위에서 yarn build로 빌드한 결과물 폴더 build를 선택합니다. 정상적으로 설치가 되면 아래 화면과 같이 방금 작성한 App이 리스트에 표시되며, 크롬 브라우저 툴바에도 아이콘이 하나 생성되었음을 확인할 수 있습니다.

Image

실행 화면은 다음과 같습니다.

Image

IntelliJ에서 타켓 디바이스 리스트 재로딩하기(Restart Flutter Daemon)

|

Restart Flutter Daemon

메뉴 찾기가 상당히 어렵습니다. 화면 우측 최상단에 있는 찾기 버튼을 클릭합니다.

Image

다이얼로그 창에서 Restart Flutter Daemon을 검색한다음 실행하면 됩니다.

Flutter Expandable ListView 예제

|

group.dart

import 'package:flutter/material.dart';

class Group {
  final String name;
  final IconData icon;
  final peopleList = List<String>();

  void addPerson(String name) {
    peopleList.add(name);
  }

  Group({this.name, this.icon});
}


main.dart

import 'package:flutter/material.dart';

import 'group.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Expandable ListView',
      theme: ThemeData(
        primarySwatch: Colors.pink,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text("Expandable ListView Example"),
        ),
        body: MyExample(),
      ),
    );
  }
}

class MyExample extends StatelessWidget {
  final Group group1 = Group(name: 'Teacher', icon: Icons.people);
  final Group group2 = Group(name: 'Student', icon: Icons.people_outline);

  final groupList = List<Group>();

  MyExample() {
    group1.addPerson('snowdeer');
    group1.addPerson('snowcat');
    group1.addPerson('snowlion');

    group2.addPerson('ran');
    group2.addPerson('song');
    group2.addPerson('down');
    group2.addPerson('john');
    group2.addPerson('yang');

    groupList.add(group1);
    groupList.add(group2);
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        margin: EdgeInsets.all(8.0),
        child: ListView.builder(
          itemCount: groupList.length,
          itemBuilder: (context, i) {
            final group = groupList[i];
            return ExpansionTile(
              title: Text(group.name),
              children: [
                Column(
                  children: _buildExpandableContent(group),
                )
              ],
            );
          },
        ),
      ),
    );
  }

  _buildExpandableContent(Group group) {
    final columnContent = List<Widget>();

    for (String name in group.peopleList)
      columnContent.add(
        ListTile(
          title: Text(
            name,
            style: TextStyle(fontSize: 18.0),
          ),
          leading: Icon(group.icon),
        ),
      );

    return columnContent;
  }
}

모던 C++ 입문 책 요약 - (3)

|

모던 C++ 입문 책을 읽고 실수하기 쉽거나 유용한 팁, 더 나은 방법 등을 정리해보았습니다.

클래스(class)와 구조체(struct)

C++에서 클래스(class)와 구조체(struct)의 유일한 차이는 구조체의 모든 멤버들의 접근 지정자가 public라는 점 뿐입니다. 가능하면 클래스를 사용하는 것이 좋으며 Getter와 Setter 함수로 데이터에 접근하는 것이 불편한 헬퍼 타입에 대해서만 구조체를 쓰는 것이 좋습니다.

friend는 클래스의 private, protected, public 데이터에 접근이 가능하기 때문에 가급적 사용하지 않는 것이 좋습니다.


Default Construct

디폴트 생성자는 인수가 없는 생성자 또는 모든 인수가 기본 값을 가지는 생성자입니다. 디폴트 생성자가 없어도 클래스를 사용할 수는 있지만 가급적 디폴트 생성자를 정의하는 편이 좋습니다. 특히 리스트, 트리, 벡터, 행렬 등 디폴트 생성자가 없는 타입의 컨테이너를 구현하는 것은 아주 번거롭기 때문에 디폴트 생성자는 가급적 정의하는 것이 좋습니다.


복사 생성자

객체를 복사하는 것보다 복사 생성자를 사용하는 방법이 더 좋습니다.

class Complex {
  public:
    Complex(const Complex & c) : i(c.i), r(c.r) {}

  private:
    // ...
}

클래스 내부의 변수는 포인터보다는 unique_ptr이 메모리 누수 예방이나 실수 방지 면에서 더 낫습니다.


생성자 위임(Delegating Constructor)

class Complex {
  public:
    Complex(double r, double i) : r{ r }, i{ i } {}
    Complex(double r) : Complex{ r, 0.0 } {}
    Complex() : Complex{ 0.0 } {}
}



소멸자

소멸자는 다음 두 가지를 유의해야 합니다.

  • 절대 Exception을 발생시키지 말아라.
  • 클래스에 virtual 함수가 포함되어 있으면 소멸자도 virtual이어야 한다.


첨자 연산자

class vector {
  public:
    double at(int i) {
      assert(i >= 0 && i < my_size);
      
      return data[i];
    }
};

와 같은 함수를 첨자 연산자를 이용해서 다시 표현할 수 있습니다.

class vector {
  public:
    double operator[](int i) {
      assert(i >= 0 && i < my_size);
      
      return data[i];
    }
};

보다 코드가 더 간결해지며 코드의 의미를 더 명확하게 표현할 수 있습니다.