目次

Effective Dart: スタイル

目次 keyboard_arrow_down keyboard_arrow_up
more_horiz

優れたコードの驚くほど重要な部分は、優れたスタイルです。一貫した命名、順序付け、およびフォーマットは、 *同じ* であるコードが *同じように見える* のに役立ちます。これは、私たちのほとんどが眼球系に持っている強力なパターンマッチングハードウェアを利用しています。Dartエコシステム全体で一貫したスタイルを使用すると、全員が互いのコードから学び、貢献しやすくなります。

識別子

#

Dartには、3種類の識別子があります。

  • UpperCamelCase 名は、最初の単語を含む各単語の最初の文字を大文字にします。

  • lowerCamelCase 名は、最初の単語を除く各単語の最初の文字を大文字にします。最初の単語は、頭字語であっても常に小文字です。

  • lowercase_with_underscores 名は、頭字語であっても小文字のみを使用し、単語を_で区切ります。

型名にはUpperCamelCaseを使用すること

#

リンタールール: camel_case_types

クラス、列挙型、型定義、および型パラメータは、各単語(最初の単語を含む)の最初の文字を大文字にし、区切り文字を使用しないでください。

良い例dart
class SliderMenu { ... }

class HttpRequest { ... }

typedef Predicate<T> = bool Function(T value);

これには、メタデータアノテーションで使用することを目的としたクラスも含まれます。

良い例dart
class Foo {
  const Foo([Object? arg]);
}

@Foo(anArg)
class A { ... }

@Foo()
class B { ... }

アノテーションクラスのコンストラクタがパラメータを取らない場合は、別のlowerCamelCase定数を作成することをお勧めします。

良い例dart
const foo = Foo();

@foo
class C { ... }

拡張機能名にはUpperCamelCaseを使用すること

#

リンタールール: camel_case_extensions

型と同様に、拡張機能は各単語(最初の単語を含む)の最初の文字を大文字にし、区切り文字を使用しないでください。

良い例dart
extension MyFancyList<T> on List<T> { ... }

extension SmartIterable<T> on Iterable<T> { ... }

パッケージ、ディレクトリ、およびソースファイルの名前には、lowercase_with_underscoresを使用すること

#

リンタールール: file_namespackage_names

ファイルシステムによっては、大文字と小文字が区別されないため、多くのプロジェクトではファイル名をすべて小文字にする必要があります。区切り文字を使用すると、その形式でも名前を読みやすくすることができます。区切り文字としてアンダースコアを使用すると、名前が有効なDart識別子のままであることが保証されます。これは、言語が後でシンボリックインポートをサポートする場合に役立つ可能性があります。

良い例
my_package
└─ lib
   └─ file_system.dart
   └─ slider_menu.dart
悪い例
mypackage
└─ lib
   └─ file-system.dart
   └─ SliderMenu.dart

インポートプレフィックス名にはlowercase_with_underscoresを使用すること

#

リンタールール: library_prefixes

良い例dart
import 'dart:math' as math;
import 'package:angular_components/angular_components.dart' as angular_components;
import 'package:js/js.dart' as js;
悪い例dart
import 'dart:math' as Math;
import 'package:angular_components/angular_components.dart' as angularComponents;
import 'package:js/js.dart' as JS;

その他の識別子名にはlowerCamelCaseを使用すること

#

リンタールール: non_constant_identifier_names

クラスメンバー、トップレベル定義、変数、パラメータ、および名前付きパラメータは、最初の単語 *を除く* 各単語の最初の文字を大文字にし、区切り文字を使用しないでください。

良い例dart
var count = 3;

HttpRequest httpRequest;

void align(bool clearItems) {
  // ...
}

定数名にはlowerCamelCaseを使用することが望ましい

#

リンタールール: constant_identifier_names

新しいコードでは、列挙値を含む定数変数にlowerCamelCaseを使用してください。

良い例dart
const pi = 3.14;
const defaultTimeout = 1000;
final urlScheme = RegExp('^([a-z]+):');

class Dice {
  static final numberGenerator = Random();
}
悪い例dart
const PI = 3.14;
const DefaultTimeout = 1000;
final URL_SCHEME = RegExp('^([a-z]+):');

class Dice {
  static final NUMBER_GENERATOR = Random();
}

以下の場合のように、既存のコードとの整合性のためにSCREAMING_CAPSを使用しても構いません。

  • すでにSCREAMING_CAPSを使用しているファイルまたはライブラリにコードを追加する場合。
  • Javaコードと並列のDartコードを生成する場合(たとえば、protobufから生成された列挙型など)。

3文字以上の頭字語や略語は単語のように大文字にすること

#

大文字の頭字語は読みにくく、複数の頭字語が隣接していると、あいまいな名前になる可能性があります。たとえば、識別子HTTPSFTPが与えられた場合、読者はそれがHTTPS FTPを指すのか、それともHTTP SFTPを指すのかを判断できません。これを避けるために、ほとんどの頭字語と略語は通常の単語のように大文字にします。この識別子は、前者を指す場合はHttpsFtp、後者を指す場合はHttpSftpになります。

2文字の略語と頭字語は例外です。英語で両方の文字が大文字になっている場合は、識別子で使用するときも両方を大文字のままにする必要があります。そうでない場合は、単語のように大文字にします。

良い例dart
// Longer than two letters, so always like a word:
Http // "hypertext transfer protocol"
Nasa // "national aeronautics and space administration"
Uri // "uniform resource identifier"
Esq // "esquire"
Ave // "avenue"

// Two letters, capitalized in English, so capitalized in an identifier:
ID // "identifier"
TV // "television"
UI // "user interface"

// Two letters, not capitalized in English, so like a word in an identifier:
Mr // "mister"
St // "street"
Rd // "road"
悪い例dart
HTTP // "hypertext transfer protocol"
NASA // "national aeronautics and space administration"
URI // "uniform resource identifier"
esq // "esquire"
Ave // "avenue"

Id // "identifier"
Tv // "television"
Ui // "user interface"

MR // "mister"
ST // "street"
RD // "road"

何らかの形の略語がlowerCamelCase識別子の先頭にある場合、略語はすべて小文字にする必要があります。

dart
var httpConnection = connect();
var tvSet = Television();
var mrRogers = 'hello, neighbor';

未使用のコールバックパラメータには___などを使用することが望ましい

#

コールバック関数の型シグネチャではパラメータが必要ですが、コールバックの実装ではパラメータを *使用しない* 場合があります。この場合、未使用のパラメータに_という名前を付けるのが慣用的です。関数に複数の未使用のパラメータがある場合は、名前の衝突を避けるために追加のアンダースコア(_____など)を使用します。

良い例dart
futureOfVoid.then((_) {
  print('Operation complete.');
});

このガイドラインは、 *匿名でローカル* の関数のみに適用されます。これらの関数は通常、未使用のパラメータが何を表しているかが明らかなコンテキストですぐに使用されます。対照的に、トップレベル関数とメソッド宣言にはそのコンテキストがないため、使用されていない場合でも、各パラメータが何のためにあるかが明らかになるように、パラメータに名前を付ける必要があります。

プライベートでない識別子に先頭にアンダースコアを使用しないこと

#

Dartは、識別子の先頭にアンダースコアを使用して、メンバーとトップレベルの宣言をプライベートとしてマークします。これにより、ユーザーは先頭のアンダースコアをこれらの種類の宣言のいずれかと関連付けるようにトレーニングされます。彼らは「_」を見て「プライベート」と考えます。

ローカル変数、パラメータ、ローカル関数、またはライブラリプレフィックスには、「プライベート」の概念はありません。これらのいずれかの名前がアンダースコアで始まる場合、読者に混乱した信号を送信します。それを避けるために、それらの名前には先頭のアンダースコアを使用しないでください。

接頭辞を使用しないこと

#

ハンガリアン記法やその他のスキームは、BCPLの時代に登場しました。その当時は、コンパイラはコードを理解するのにそれほど役立ちませんでした。Dartは、宣言の型、スコープ、変更可能性、およびその他のプロパティを伝えることができるため、これらのプロパティを識別子名にエンコードする理由はありません。

良い例dart
defaultTimeout
悪い例dart
kDefaultTimeout

ライブラリに明示的に名前を付けないこと

#

libraryディレクティブに名前を追加することは技術的には可能ですが、レガシー機能であり、推奨されません。

Dartは、パスとファイル名に基づいて各ライブラリに一意のタグを生成します。ライブラリに名前を付けると、この生成されたURIがオーバーライドされます。URIがないと、ツールが問題のメインライブラリファイルを見つけるのが難しくなる可能性があります。

悪い例dart
library my_library;
良い例dart
/// A really great test library.
@TestOn('browser')
library;

順序

#

ファイルのプリアンブルを整理するために、ディレクティブが表示されるべき順序が規定されています。各「セクション」は空白行で区切る必要があります。

すべての順序付けガイドラインは、単一のリンタールール directives_ordering によって処理されます。

**推奨:** dart: インポートを他のインポートの前に配置します。

#

リンタールール: directives_ordering

良い例dart
import 'dart:async';
import 'dart:html';

import 'package:bar/bar.dart';
import 'package:foo/foo.dart';

**推奨:** package: インポートを相対インポートの前に配置します。

#

リンタールール: directives_ordering

良い例dart
import 'package:bar/bar.dart';
import 'package:foo/foo.dart';

import 'util.dart';

すべてのインポートの後、別のセクションにエクスポートを指定すること

#

リンタールール: directives_ordering

良い例dart
import 'src/error.dart';
import 'src/foo_bar.dart';

export 'src/error.dart';
悪い例dart
import 'src/error.dart';
export 'src/error.dart';
import 'src/foo_bar.dart';

セクションをアルファベット順にソートすること

#

リンタールール: directives_ordering

良い例dart
import 'package:bar/bar.dart';
import 'package:foo/foo.dart';

import 'foo.dart';
import 'foo/foo.dart';
悪い例dart
import 'package:foo/foo.dart';
import 'package:bar/bar.dart';

import 'foo/foo.dart';
import 'foo.dart';

フォーマット

#

多くの言語と同様に、Dartは空白を無視します。しかし、*人間*は無視しません。一貫した空白スタイルを持つことで、人間がコードをコンパイラと同じように見ることができるようになります。

**推奨:** dart format を使用してコードをフォーマットします。

#

フォーマットは面倒な作業であり、特にリファクタリング中は時間がかかります。幸いなことに、それについて心配する必要はありません。Dartには、自動的にコードをフォーマットする洗練された dart format が用意されています。適用されるルールについてはドキュメントがありますが、Dartの公式の空白処理ルールは、*`dart format` が生成するもの*です。

残りのフォーマットガイドラインは、dart format が修正できない少数の項目に関するものです。

フォーマッターに優しいコードに変更することを検討すること

#

フォーマッターは、どのようなコードに対しても最善を尽くしますが、奇跡を起こすことはできません。コードに特に長い識別子、深くネストされた式、さまざまな種類の演算子が混在している場合など、フォーマットされた出力は依然として読みづらい可能性があります。

そのような場合は、コードを再構成または簡略化します。ローカル変数名を短縮したり、式を新しいローカル変数に抽出することを検討してください。言い換えれば、コードを手動でフォーマットし、より読みやすくしようとする場合と同じ種類の変更を加えます。dart format は、美しいコードを作成するために、時には反復的に協力するパートナーと考えてください。

80文字を超える行は避けること

#

リンタールール: lines_longer_than_80_chars

可読性に関する研究によると、長いテキスト行は、次の行の先頭に移動するときに目が遠くまで移動する必要があるため、読みづらいことがわかっています。これが、新聞や雑誌が複数のテキスト列を使用する理由です。

どうしても80文字より長い行が必要な場合は、経験上、コードが冗長すぎて、もう少しコンパクトにできる可能性があります。主な原因は、通常、VeryLongCamelCaseClassNames です。その型名の各単語は、重要な情報を伝えているか、名前の衝突を防いでいるか、自問してみてください。そうでない場合は、省略することを検討してください。

dart format はこの作業の99%を自動的に行いますが、残りの1%は手動で行う必要があります。長い文字列リテラルを80列に収まるように分割することはしないため、手動で行う必要があります。

**例外:** URIまたはファイルパスがコメントまたは文字列(通常はimportまたはexport)に含まれている場合、行が80文字を超えても、そのままにしておくことができます。これにより、ソースファイル内でパスを検索しやすくなります。

**例外:** 複数行の文字列には、80文字を超える行を含めることができます。これは、文字列内では改行が重要であり、行を短い行に分割するとプログラムが変更される可能性があるためです。

すべての制御フロー文に中括弧を使用すること

#

リンタールール: curly_braces_in_flow_control_structures

そうすることで、ぶら下がりelse 問題を回避できます。

良い例dart
if (isWeekDay) {
  print('Bike to work!');
} else {
  print('Go dancing or read a book!');
}

**例外:** else 節のない if 文があり、if 文全体が1行に収まる場合は、必要に応じて中括弧を省略できます。

良い例dart
if (arg == null) return defaultValue;

ただし、本体が次の行に折り返される場合は、中括弧を使用します。

良い例dart
if (overflowChars != other.overflowChars) {
  return overflowChars < other.overflowChars;
}
悪い例dart
if (overflowChars != other.overflowChars)
  return overflowChars < other.overflowChars;