2022/5/31/Dart/EffectiveDart/DesignPart2

 

<<パート1へ

Contents

Libraries

A leading underscore character ( _ ) indicates that a member is private to its library. This is not mere convention, but is built into the language itself.

先頭のアンダースコア文字( _ )は、そのメンバーがそのライブラリに対してプライベートであることを示します。これは単なる慣習ではなく、言語自体に組み込まれているものです。

ここでのライブラリプライベート=ファイルプライベート


PREFER making declarations private.

宣言はプライベートにしましょう。

A public declaration in a library—either top level or in a class—is a signal that other libraries can and should access that member. It is also a commitment on your library’s part to support that and behave properly when it happens.

トップレベルであれクラス内であれ、ライブラリ内でのパブリックな宣言は、他のライブラリがそのメンバに対してアクセスできるしアクセスするべきである、ということを示しています。

それをサポートし、それが発生したときに適切に動作することも、ライブラリ側の取り組みです。

 

If that’s not what you intend, add the little _ and be happy. Narrow public interfaces are easier for you to maintain and easier for users to learn.

もしそれがあなたの意図するところでないなら、小さな_を追加して、満足してください。狭い公開インターフェースは、あなたにとってメンテナンスがしやすく、ユーザーにとっても学習しやすいものです。

 

As a nice bonus, the analyzer will tell you about unused private declarations so you can delete dead code.

アナライザが未使用のプライベートなメンバの宣言を教えてくれるので、それによってdead codeを削除できる、というボーナスもあります。

 

It can’t do that if the member is public because it doesn’t know if any code outside of its view is using it.

メンバーがパブリックの場合、外部のコードがそのメンバーを使用しているかどうかがわからないので、それは不可能です。


CONSIDER declaring multiple classes in the same library.

一つのライブラリに複数のクラスを宣言することを検討しましょう。

Some languages, such as Java, tie the organization of files to the organization of classes—each file may only define a single top level class.

Javaなどのいくつかの言語ではファイルの構成とクラスの構成を結びつけ、1つのファイルに一つのトップレベルのクラスを定義するようにしています。

 

Dart does not have that limitation. Libraries are distinct entities separate from classes.

Dartにはその制限はありません。 ライブラリは、クラスとは別のエンティティです。

 

It’s perfectly fine for a single library to contain multiple classes, top level variables, and functions if they all logically belong together.

1つのライブラリに複数のクラス、トップレベルの変数や関数がすべて論理的に属している場合は、それらを(1つのライブラリに)含めることはまったく問題ありません。

 

Placing multiple classes together in one library can enable some useful patterns.

複数のクラスを一つのライブラリに含めることはいくつかの有用なパターンを可能にします。

 

Since privacy in Dart works at the library level, not the class level, this is a way to define “friend” classes like you might in C++.

Dartでのプライベートはクラスレベルではなくライブラリレベルなので、これはもしかしたらあなたがC++でやっていたような「友達の」クラスを定義する方法として使えます。

 

Every class declared in the same library can access each other’s private members, but code outside of that library cannot.

同じライブラリ内のそれぞれのクラスは、(ライブラリ内の)別のプライベートメンバーにアクセスできますが、ライブラリの外のコードからはアクセスできません。

 

Of course, this guideline doesn’t mean you should put all of your classes into a huge monolithic library, just that you are allowed to place more than one class in a single library.

もちろん、このガイドラインは、すべてのクラスを巨大なモノリシックライブラリに配置する必要があることを意味するのではなく、1つのライブラリに複数のクラスを配置できることを意味します。




Classes and mixins

Dart is a “pure” object-oriented language in that all objects are instances of classes. But Dart does not require all code to be defined inside a class—you can define top-level variables, constants, and functions like you can in a procedural or functional language.

Dartは、すべてのオブジェクトがクラスのインスタンスであるという点で、「純粋な」オブジェクト指向の言語です。しかし、Dartでは、すべてのコードをクラス内で定義する必要はありません。手続き型言語や関数型言語のように、トップレベルの変数、定数、関数を定義することができます。


AVOID defining a one-member abstract class when a simple function will do.

シンプルな関数定義で済むのに、抽象クラスを定義するのは避けましょう。

Linter rule: one_member_abstracts

Unlike Java, Dart has first-class functions, closures, and a nice light syntax for using them.

Javaとは異なり、Dartにはファーストクラスの関数、クロージャ、およびそれらを使用するための優れた軽い構文があります。

このガイドラインの内容と直接的な関連は薄いですが、”first-class function”とはについて。

A programming language is said to have First-class functions when functions in that language are treated like any other variable. For example, in such a language, a function can be passed as an argument to other functions, can be returned by another function and can be assigned as a value to a variable.

プログラミング言語では、その言語の関数が変数と同じように扱われる場合、第一級関数を持っていると言われます。 たとえば、このような言語では、関数を引数として他の関数に渡したり、別の関数から返したり、変数に値として割り当てたりすることができます。

https://developer.mozilla.org/en-US/docs/Glossary/First-class_Function

結局のところ「first-class function = 無名関数、クロージャー」という理解で問題無いようです。イコールというか、「Dartの関数(無名関数・クロージャー)はfirst-class functionの定義に当てはまる」ということだと思われます。

 

If all you need is something like a callback, just use a function.

あなたの必要なものがコールバックのようなもののみなら、関数を使いましょう。

 

If you’re defining a class and it only has a single abstract member with a meaningless name like call or invoke, there is a good chance you just want a function.

もしあなたが定義したクラスに抽象メソッド一つだけがありあまり意味がないcallやinvokeなどの名前であるなら、関数定義にしたほうが良いでしょう。

//good
typedef Predicate<E> = bool Function(E element);

 

//bad
abstract class Predicate<E> {
  bool test(E element);
}

 


AVOID defining a class that contains only static members.

staticなメンバーのみのクラスを定義するのは避けましょう。

Linter rule: avoid_classes_with_only_static_members

In Java and C#, every definition must be inside a class, so it’s common to see “classes” that exist only as a place to stuff static members.

JavaやC#では全ての定義がクラス内になければなりません、ですからstaticなメンバーを定義するためだけに存在するクラスを見ることは珍しくありません。

 

Other classes are used as namespaces—a way to give a shared prefix to a bunch of members to relate them to each other or avoid a name collision.

あるいは名前空間として定義されているクラス。

名前空間とは、それぞれを関連づけるために、あるいは名前の衝突を防ぐために、多くのメンバーに対して共通のプレフィックスを与える方法。

 

Dart has top-level functions, variables, and constants, so you don’t need a class just to define something.

Dartにはトップレベル関数、トップレベル変数、トップレベル定数の仕組みがあります。ですから、何かを定義するだけの目的でクラスを用意する必要はありません。

 

If what you want is a namespace, a library is a better fit.

名前空間が必要なら、ライブラリを利用しましょう。

 

Libraries support import prefixes and show/hide combinators. Those are powerful tools that let the consumer of your code handle name collisions in the way that works best for them.

ライブラリはインポートプレフィックスとshow/hideコンビネーターをサポートしています。これらはあなたのコードを利用する人がベストな方法で名前衝突を防止するための強力なツールです。

 

If a function or variable isn’t logically tied to a class, put it at the top level. If you’re worried about name collisions, give it a more precise name or move it to a separate library that can be imported with a prefix.

もし関数や変数がクラスと論理的に結びつかないのなら、トップレベルに定義しておきましょう。

名前の衝突が心配な場合は、より正確な名前を付けるか、プレフィックスを付けてインポートできる別のライブラリに移動してください。

//good
DateTime mostRecent(List<DateTime> dates) {
  return dates.reduce((a, b) => a.isAfter(b) ? a : b);
}

const _favoriteMammal = 'weasel';

 

//bad
class DateUtils {
  static DateTime mostRecent(List<DateTime> dates) {
    return dates.reduce((a, b) => a.isAfter(b) ? a : b);
  }
}

class _Favorites {
  static const mammal = 'weasel';
}

In idiomatic Dart, classes define kinds of objects. A type that is never instantiated is a code smell.

慣用的なDartでは、クラスがオブジェクトの種類を定義します。インスタンス化されることのない型は、コードの臭いの元となる。

 

However, this isn’t a hard rule. With constants and enum-like types, it may be natural to group them in a class.

しかしこれは厳格なルールではありません。いくつかの定数を持つ列挙型のような型は、それらをクラスとしてグループ化するのが自然な場合もあります。

//good
class Color {
  static const red = '#f00';
  static const green = '#0f0';
  static const blue = '#00f';
  static const black = '#000';
  static const white = '#fff';
}

 


AVOID extending a class that isn’t intended to be subclassed.

サブクラス化されることを意図していないクラスを拡張するのは避けましょう。

 

 

 


DO document if your class supports being extended.

This is the corollary to the above rule. If you want to allow subclasses of your class, state that. Suffix the class name with Base, or mention it in the class’s doc comment.

これは、上記のルールの帰結です。もし、自分のクラスのサブクラスを許可したいのであれば、その旨を明記します。クラス名の最後に Base を付けるか、クラスのドキュメントコメントでその旨を記述します。


AVOID implementing a class that isn’t intended to be an interface.

インターフェースを意図していないクラスを実装するのは避けてください。

 

Implicit interfaces are a powerful tool in Dart to avoid having to repeat the contract of a class when it can be trivially inferred from the signatures of an implementation of that contract.

暗黙的なインターフェースは、クラスのコントラクトの実装のシグネチャから簡単に推測できる場合に、クラスのコントラクトを繰り返す必要をなくすためのDartの強力なツールです。

(抽象クラスを定義する手間が省けますね、ということ)

 

But implementing a class’s interface is a very tight coupling to that class.

ただし、クラスのインターフェースを実装することは、そのクラスと非常に緊密な連携を持つことになります。

 

It means virtually any change to the class whose interface you are implementing will break your implementation.

それはあるインターフェースを実装しているクラスへの事実上のあらゆる変更は、その実装を破壊することである、ということを意味します。

 

For example, adding a new member to a class is usually a safe, non-breaking change.

例えば、クラスへ新しいメンバーを追加することは通常は安全であり、破壊的な変更ではありません。

 

But if you are implementing that class’s interface, now your class has a static error because it lacks an implementation of that new method.

しかしそのクラスのインターフェースを実装すると、「新しいメソッドの実装が無い」という理由であなたのクラスは静的エラーを出します。

この文での「実装する」は、具体的には、「インターフェースに新しいメンバーを追加すること」、だと思われる。

 

Library maintainers need the ability to evolve existing classes without breaking users.

ライブラリのメンテナーには、(ライブラリの)ユーザーのコードを破壊することなく既存のクラスを進化させる手段が必要です。

 

If you treat every class like it exposes an interface that users are free to implement, then changing those classes becomes very difficult.

すべてのクラスを「ユーザーが自由に実装できるインターフェースとして公開しているもの」として扱うと、(ライブラリのメンテナーが)それらのクラスを変更することは非常に困難になります。

変更したら、そのクラスをimplements(実装)しているクラス(実装先のクラス)のコードが壊れるから。

 

That difficulty in turn means the libraries you rely on are slower to grow and adapt to new needs.

その困難は、あなたが頼っているライブラリの成長や新しいニーズへの対応が遅くなることにつながります。

 

To give the authors of the classes you use more leeway, avoid implementing implicit interfaces except for classes that are clearly intended to be implemented.

クラスの作者に(実装先を気にせずそのクラスを自由に変更する)裁量を与えるために、(明らかに実装されることが意図されている場合を除き)暗黙的なインターフェースの仕組みによって実装することは避けましょう。

 

Otherwise, you may introduce a coupling that the author doesn’t intend, and they may break your code without realizing it.

そうしないと、作者が意図していない「結合」があなたのコードに導入されることになり、それはあなたが気づかないうちにあなたのコードを破壊する可能性があります。


DO document if your class supports being used as an interface.

インターフェースとして使用されることを意図しているクラスはドキュメントでその旨示しましょう。

 

If your class can be used as an interface, mention that in the class’s doc comment.

あなたのクラスが実装されることを意図しているものなら、クラスのドキュメントコメントでそれを明示しましょう。


DO use mixin to define a mixin type.

mixin型として定義する場合mixinキーワードを使用しましょう。

Linter rule: prefer_mixin

Dart originally didn’t have a separate syntax for declaring a class intended to be mixed in to other classes.

Dartは元々、別のクラスにmixされることを意図した、それ用のシンタックスは用意されていませんでした。

 

Instead, any class that met certain restrictions (no non-default constructor, no superclass, etc.) could be used as a mixin.

その代わり、一定の制限(デフォルト以外のコンストラクタを持たない、スーパークラスを持たないなど)を満たしたクラスは、mixinとして使用することができました。

 

This was confusing because the author of the class might not have intended it to be mixed in.

クラスの作者がmixinとして使用されることを意図していない可能性があるので、このルールは混乱を招くものでした。

 

Dart 2.1.0 added a mixin keyword for explicitly declaring a mixin.

Dart 2.1.0でmixinを定義するためのmixinキーワードが追加されました。

 

Types created using that can only be used as mixins, and the language also ensures that your mixin stays within the restrictions. When defining a new type that you intend to be used as a mixin, use this syntax.

このキーワードを使って定義された型はmixinとしてのみ使用できます。そしてまた、この言語は、mixinが制約の範囲内に収まるようにします。mixiとして使用する予定の新しい型を定義するときは、この構文を使用してください。

//good
mixin ClickableMixin implements Control {
  bool _isDown = false;

  void click();

  void mouseDown() {
    _isDown = true;
  }

  void mouseUp() {
    if (_isDown) click();
    _isDown = false;
  }
}

 


AVOID mixing in a type that isn’t intended to be a mixin.

mixinとして使用することを意図していないクラスをmix inするのはやめましょう。

Linter rule: prefer_mixin

For compatibility, Dart still allows you to mix in classes that aren’t defined using mixin. However, that’s risky.

互換性のため、Dartは現在もmixinキーワードを使用せずに定義されたクラスをmixinすることを許可しています。しかしそれはリスクが高いです。

 

If the author of the class doesn’t intend the class to be used as a mixin, they might change the class in a way that breaks the mixin restrictions.

クラスの作者が、mixinとして使用されることを意図せずにクラスを定義している場合、彼らはそのクラスをmixinの制約から外れる形で変更する可能性があります。

 

For example, if they add a constructor, your class will break.

例えば、彼らがコンストラクタを追加した場合、(mixin先の)あなたのクラスが破壊されます(辻褄が合わなくなりエラーが発生する)。

 

If the class doesn’t have a doc comment or an obvious name like IterableMixin, assume you cannot mix in the class if it isn’t declared using mixin.

そのクラスがドキュメントコメントが無く、型名がIterableMixinのように明らかなものでも無い場合で尚且つmixinキーワードを使用していない場合、そのクラスはmixinとして使用できない、と考えましょう。




Constructors

コンストラクタ

Dart constructors are created by declaring a function with the same name as the class and, optionally, an additional identifier. The latter are called named constructors.

Dartのコンストラクタは、クラス名と同じ名前を使って宣言します。クラス名と一緒に追加の識別子を使って宣言することもできます。これを名前付きコンストラクタと呼びます。


CONSIDER making your constructor const if the class supports it.

そのクラスがサポートする場合、constコンストラクタにすることを検討してください。

If you have a class where all the fields are final, and the constructor does nothing but initialize them, you can make that constructor const.

あなたのクラスの全てのフィールドがfinalで、イニシャライザはフィールドを初期化するだけの場合、そのコンストラクタをconstキーワードを使って宣言できます。

 

That lets users create instances of your class in places where constants are required—inside other larger constants, switch cases, default parameter values, etc.

これにより、ユーザーは定数が必要な場所(他の大きな定数(const)の内部、スイッチケース、パラメータのデフォルト値など)で、クラスのインスタンスを作成することができます。

定数(const)がインスタンスを生成できたとしたら、自動的にそのインスタンスはconstなインスタンス、ということになる。

 

If you don’t explicitly make it const, they aren’t able to do that.

明示的にconstキーワードを使用せずに定義されたコンストラクタはそれができません。

 

Note, however, that a const constructor is a commitment in your public API. If you later change the constructor to non-const, it will break users that are calling it in constant expressions.

しかし、constコンストラクタはあなたのパブリックなAPIの約束がとなります。最初にconstコンストラクタとして定義していたのに、後になってそのコンストラクタをnon-constコンストラクタに変更した場合、それはユーザーのコード(constな式の中で使用されているconstコンストラクタ呼び出し箇所)を破壊することになります。

 

If you don’t want to commit to that, don’t make it const. In practice, const constructors are most useful for simple, immutable value-like types.

それを約束したくない場合は、constコンストラクタにしないでください。 実際には、constコンストラクターは、単純で不変の値のような型に最も役立ちます。




Members

A member belongs to an object and can be either methods or instance variables.

メンバーはオブジェクトに属している、メソッドかインスタンス変数です。


PREFER making fields and top-level variables final.

フィールドとトップレベル変数をfinalで宣言しましょう。

Linter rule: prefer_final_fields

State that is not mutable—that does not change over time—is easier for programmers to reason about.

mutable(可変)ではない状態、つまりずっと値が変わらないこと、はプログラマーにとって理解しやすい、わかりやすいことです。

 

Classes and libraries that minimize the amount of mutable state they work with tend to be easier to maintain.

扱うべき可変な状態を最小化したクラスやライブラリはメンテナンスしやすいです。

 

Of course, it is often useful to have mutable data. But, if you don’t need it, your default should be to make fields and top-level variables finalwhen you can.

もちろん時にはmutableなデータを保持することが有効な場合もあります。しかし、mutableにする必要がない場合、基本的にトップレベル変数やフィールドはfinalキーワードで宣言するようにしましょう。

 

Sometimes an instance field doesn’t change after it has been initialized, but can’t be initialized until after the instance is constructed.

時には初期化後にインスタンスフィールドの値が変更しないこともあるでしょう、しかし、インスタンスフィールドはインスタンスが生成(構築)されるまでは初期化できません。

 

For example, it may need to reference this or some other field on the instance. In cases like that, consider making the field late final. When you do, you may also be able to initialize the field at its declaration.

具体的には、(フィールドの初期化時に)this」や「インスタンスの他のフィールド」を参照する必要がある場合です。このようなケースではフィールドをlate finalにしてください。そうすれば、宣言時にフィールドを初期化することができます。

lateキーワードをつければ初期化が「初めてアクセスされる時点(つまりインスタンス生成完了後)」まで遅延される。

 


DO use getters for operations that conceptually access properties.

概念的にプロパティにアクセスする操作には、ゲッターを使用してください。

 

Deciding when a member should be a getter versus a method is a subtle but important part of good API design,hence this very long guideline.

メンバーをゲッターにするか、メソッドにするかは些細なことですが、しかし良いAPIを設計するためには重要なことです。

 

Some other language’s cultures shy away from getters. They only use them when the operation is almost exactly like a field—it does a minuscule amount of calculation on state that lives entirely on the object.

他のいくつかの言語の文化は、ゲッターを敬遠しています。 これらは、操作がフィールドとほぼ同じである場合にのみ使用します。つまり、オブジェクト上に完全に存在する状態に対して、ごくわずかな量の計算を実行する場合です。

 

Anything more complex or heavyweight than that gets () after the name to signal “computation goin’ on here!” because a bare name after a . means “field”.

それよりも複雑または重いものは、名前の後に「計算がここにあります!」という信号として()を付けます。 .の後に識別子があると 「フィールド」を意味するからです。

 

Dart is not like that. In Dart, all dotted names are member invocations that may do computation.

Dartでは違います。Dartでは全ての「.識別子」は計算をする可能性のあるmember invocation

 

Fields are special—they’re getters whose implementation is provided by the language.

フィールドは、「実装がDart言語によって提供されるgetter」という点で特別な存在です。

 

In other words, getters are not “particularly slow fields” in Dart; fields are “particularly fast getters”.

言い換えれば、ゲッターはダートでは「特に遅いフィールド」ではありません。 フィールドは「特に高速なゲッター」なのです。

 

Even so, choosing a getter over a method sends an important signal to the caller. The signal, roughly, is that the operation is “field-like”. The operation, at least in principle, could be implemented using a field, as far as the caller knows. That implies:

ただ、そうであっても、メソッドではなくゲッターという形を選択したことは呼び出し元にとって重要なシグナルです。簡単に言えば、そのシグナルとは、ゲッターを選択したということは”field-likeである”ということです。原則として、呼び出し元の知る限りは、その処理はフィールドを使って実装されたものです。これは下記のことを示唆します。

  • The operation does not take any arguments and returns a result.

その処理は引数を取らず、結果を返すものだ。

The caller cares mostly about the result.

呼び出し元の関心ごとはほとんどその結果である。

If you want the caller to worry about how the operation produces its result more than they do the result being produced, then give the operation a verb name that describes the work and make it a method.

呼び出し元が、生成される結果よりも操作が結果を生成する方法について心配する場合は、操作に作業を説明する動詞名を付けて、メソッドにしましょう。

 

This does not mean the operation has to be particularly fast in order to be a getter.

ゲッターであるためにはその処理は早くなければならない、ということではありません。

 

IterableBase.length is O(n), and that’s OK.

IterableBase.lengthO(n)ですが、それで問題ありません。

 

It’s fine for a getter to do significant calculation. But if it does a surprising amount of work, you may want to draw their attention to that by making it a method whose name is a verb describing what it does.

ゲッターが多くの計算を行ってもいいのです。しかし、それが驚くほどの量の作業を行う場合は、それが何をするかを説明する動詞を名前とするメソッドにすることで、それに注意を向けたいと思うかもしれません。

//bad
connection.nextIncomingMessage; // Does network I/O.
expression.normalForm; // Could be exponential to calculate.

ネットワークI/Oの処理(非常に時間がかかる可能性がある)や指数関数的な処理量になる可能性があるものはメソッドにしたほうがよい。


The operation does not have user-visible side effects.

その処理はユーザーが認識できるようなサイドエフェクト(副作用)を持っていない。

副作用とは、「関数が入力(引数)を受け取って出力(返り値)を返す」ことをその関数の主な作用と捉えて、その主な作用以外の処理のことを副作用と呼びます。

詳しくはこちらをどうぞ。(wikipedia)

Accessing a real field does not alter the object or any other state in the program. It doesn’t produce output, write files, etc. A getter shouldn’t do those things either.

フィールドにアクセスすることはオブジェクトやその他のプログラムの状態を変えません。出力を生み出しませんし、ファイルに書き込んだりもしません。ゲッターはこれらのことをすべきではありません。

 

The “user-visible” part is important. It’s fine for getters to modify hidden state or produce out of band side effects. Getters can lazily calculate and store their result, write to a cache, log stuff, etc. As long as the caller doesn’t careabout the side effect, it’s probably fine.

“user-visible”の部分は重要です。ゲッターが非表示の状態を変更したり、out-of-band(不定期)の副作用を生成したりすることは問題ありません。 ゲッターは、結果を遅延して計算して保存したり、キャッシュに書き込んだり、ログに記録したりできます。発信者が副作用を気にしない限り、おそらく問題ありません。

//bad
stdout.newline; // Produces output.
list.clear; // Modifies object.

 


The operation is idempotent.

処理がidempotent(冪等)であること。

“Idempotent” is an odd word that, in this context, basically means that calling the operation multiple times produces the same result each time, unless some state is explicitly modified between those calls.

「べき等」は奇妙な言葉であり、この文脈では、基本的に、操作を複数回呼び出すと、それらの呼び出し間で状態が明示的に変更されない限り、毎回同じ結果が生成されることを意味します。

 

(Obviously, list.length produces different results if you add an element to the list between calls.)

(当然ですが、呼び出しの間に要素をリストに追加すると、list.lengthは異なる結果を生成します。)

 

“Same result” here does not mean a getter must literally produce an identical object on successive calls.

ここでの「同じ結果」は、ゲッターが連続する呼び出しで文字通り同じオブジェクトを生成する必要があることを意味するものではありません。

 

Requiring that would force many getters to have brittle caching, which negates the whole point of using a getter.

これを要求すると、多くのゲッターに脆弱なキャッシングが強制され、ゲッターを使用することの全体的な意味が無効になります。

 

It’s common, and perfectly fine, for a getter to return a new future or list each time you call it. The important part is that the future completes to the same value, and the list contains the same elements.

ゲッターが呼び出されるたびに新しいFutureやListを返すことは一般的で、まったく問題ありません。 重要な部分は、utureが同じ値で完了し、Listに同じ要素が含まれていることです。

 

In other words, the result value should be the same in the aspects that the caller cares about.

言い換えれば、呼び出し元にとって意味のある結果の値が同じである必要がある、ということです。

//bad
DateTime.now; // New result each time.

上記のメンバnowは呼び出される都度その時の時刻を返すものなので、つまり毎回新しい結果が返されるべきものなので、ゲッターではなく、メソッドとして提供しましょう、ということ。


The resulting object doesn’t expose all of the original object’s state.

(ゲッターの)結果のオブジェクトはそのオブジェクトの全ての状態を公開するものではない。

A field exposes only a piece of an object. If your operation returns a result that exposes the original object’s entire state, it’s likely better off as a to___() or as___() method.

フィールドは、オブジェクトの一部のみを公開します。 操作が元のオブジェクトの状態全体を公開する結果を返す場合は、to ___()またはas ___()メソッドとして使用することをお勧めします。

 

If all of the above describe your operation, it should be a getter. It seems like few members would survive that gauntlet, but surprisingly many do. Many operations just do some computation on some state and most of those can and should be getters.

処理が上記の全てに当てはまるならそれはゲッターにすべきです。当てはまるものは少ないように見えるかもしれませんが、驚くべきことに多くが当てはまります。多くの処理が単にいくつかの計算をいくつかの状態に対して行うもので、そのほとんどがゲッターにできるものです。

//good
rectangle.area;
collection.isEmpty;
button.canShow;
dataSet.minimumValue;

 

 


DO use setters for operations that conceptually change properties.

概念的にプロパティを変更する操作にはセッターを使ってください。

Linter rule: use_setters_to_change_properties

Deciding between a setter versus a method is similar to deciding between a getter versus a method. In both cases, the operation should be “field-like”.

「セッターか、メソッドか」の決定は、「ゲッターか、メソッドか」の場合と似ています。どちらのケースも処理が”field-like”であるべきです。

For a setter, “field-like” means:

セッターにとって“field-like”とは、

  • The operation takes a single argument and does not produce a result value.

その処理は一つの引数を取り、結果の値を返さない。

  • The operation changes some state in the object.

オブジェクトのある状態を変更する。

The operation is idempotent.

その処理は冪等である。

 

Calling the same setter twice with the same value should do nothing the second time as far as the caller is concerned.同じセッターを同じ値で2回呼び出しても、呼び出し元にとっては、2回目は何も実行されないのと同じことです。

 

Internally, maybe you’ve got some cache invalidation or logging going on. That’s fine. But from the caller’s perspective, it appears that the second call does nothing.

内部的には、キャッシュの無効化またはログ記録が行われている可能性があります。 それはいいです。 しかし、発信者の観点からは、2番目の呼び出しは何もしないように見えます。

//good
rectangle.width = 3;
button.visible = false;

 

 


DON’T define a setter without a corresponding getter.

対応するゲッターが無いものにセッターを定義しないでください。

Linter rule: avoid_setters_without_getters

Users think of getters and setters as visible properties of an object. A “dropbox” property that can be written to but not seen is confusing and confounds their intuition about how properties work. For example, a setter without a getter means you can use = to modify it, but not +=.

ユーザーは、ゲッターとセッターをオブジェクトの表示可能なプロパティと見なします。 書き込むことはできるが表示されない「ドロップボックス」プロパティは混乱を招き、プロパティがどのように機能するかについての直感を混乱させます。 たとえば、ゲッターのないセッターは、=を使用して変更できるが、+=は使用できないことを意味します。

 

This guideline does not mean you should add a getter just to permit the setter you want to add. Objects shouldn’t generally expose more state than they need to. If you have some piece of an object’s state that can be modified but not exposed in the same way, use a method instead.

このガイドラインは、追加したいセッターを許可するためだけにゲッターを追加する必要があるという意味ではありません。 オブジェクトは通常、必要以上の状態を公開するべきではありません。 オブジェクトの状態の一部を変更できるが公開できないものにしたい場合は、代わりにメソッドを使用してください。

 


AVOID using runtime type tests to fake overloading.

フェイクオーバーローディングの真似をするために、実行時の型チェックを行うのを避けてください。

 

 


AVOID public late final fields without initializers.

初期化無しのパブリックなlate finalフィールドは避けてください。

Unlike other final fields, a late final field without an initializer does define a setter. If that field is public, then the setter is public. This is rarely what you want.

他のfinalなフィールドとは異なり、初期化子のないlate finalフィールドはセッターを定義します。 そのフィールドがパブリックの場合、セッターはパブリックです。 これはめったにあなたが望むものではありません。

 

Fields are usually marked late so that they can be initialized internally at some point in the instance’s lifetime, often inside the constructor body.

フィールドは通常、多くの場合コンストラクター本体の内部で、インスタンスの存続期間のある時点で内部的に初期化できるようにすることを目的として、lateとしてマークされます。

 

Unless you do want users to call the setter, it’s better to pick one of the following solutions:

あなたがユーザーに対してセッターを使って欲しいと考えている場合を除いて、下記の方法を選択したほうが良いでしょう。

  • Don’t use late.

lateキーワードを使わない。

  • Use late, but initialize the late field at its declaration.

lateキーワードを使うが、lateなフィールドを宣言時に初期化する。

  • Use late, but make the late field private and define a public getter for it.

lateキーワードを使うが、lateなフィールドをプライベートにして、それ用のパブリックなゲッターを用意する。

 


AVOID returning nullable Future, Stream, and collection types.

nullableなFuture,Stream,コレクション型を返すのは避けてください。

When an API returns a container type, it has two ways to indicate the absence of data: It can return an empty container or it can return null.

APIがコンテナタイプを返す場合、データがないことを示す2つの方法があります。空のコンテナを返すか、nullを返すことができます。

 

Users generally assume and prefer that you use an empty container to indicate “no data”. That way, they have a real object that they can call methods on like isEmpty.

ユーザーは通常、「データがない」ことを示すために空のコンテナーを使用することを想定し、好みます。 そうすれば、ユーザーはisEmptyのようにメソッドを呼び出すことができる実際のオブジェクトを持つことになります。

 

To indicate that your API has no data to provide, prefer returning an empty collection, a non-nullable future of a nullable type, or a stream that doesn’t emit any values.

APIに提供するデータがないことを示すには、空のコレクション、(Future?じゃなくて)Future<SomeType?>型、または値を出力しないStreamを返すことをお勧めします。

Exception: If returning null means something different from yielding an empty container, it might make sense to use a nullable type.

例外:nullを返すことは、空のコンテナーを生成することとは異なることを意味する場合、null許容型を使用することが理にかなっている可能性があります。

 


AVOID returning this from methods just to enable a fluent interface.

流暢なインターフェイスを実現するために、メソッドからthisを返すコードを書くのはやめましょう。

Linter rule: avoid_returning_this

Method cascades are a better solution for chaining method calls.

カスケード記法を使いましょう。

//good
var buffer = StringBuffer()
  ..write('one')
  ..write('two')
  ..write('three');

 

//bad
var buffer = StringBuffer()
    .write('one')
    .write('two')
    .write('three');

 




パート3へ>>

 

参考

design

カテゴリーDart

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です