一部の最新言語(Swift、Dartなど)は、protected
アクセス修飾子キーワードをサポートしていません。 Swiftはプロトコル指向の言語ですが、Dartは完全にオブジェクト指向の言語であると聞きました。
これらの現代の言語がprotected
をサポートしないのはなぜですか?完全なオブジェクト指向プログラミングにはprivate
とpublic
だけが必要ですか?
親クラスから子クラスに渡したいデータやインターフェースがいくつかある場合、protected
アクセス修飾子キーワードを用意すると便利だと思います。なぜいくつかの現代の言語はprotected
をサポートしないのですか?
それはあなたが「必須」で何を意味するかによります。
アクセス修飾子は必須ではありません。すべてのアクセス修飾子をpublic
で置き換えることができます。ほとんどのアプリケーションは、さまざまなアクセス修飾子を使用したときと同じように機能し、コンパイラの主な目的(動作するアプリケーションの出力)がアクセス修飾子に直接依存しないことを証明します。 。
Deliothがコメントで述べたように、JavascriptとPythonは、OOPの機能はまだありますが、アクセス修飾子の概念はありません。 OOPアクセス修飾子は必要ありません。
ただし、間違いを避けたい場合は、開発者の観点から修飾子にアクセスすることが非常に重要です。アクセス制限の欠如は、開発者がすべきではない依存関係に直接アクセスすることにつながり(たとえば、検証/承認レイヤーを回避する)、これはバグにつながり、時間と労力が費やされます。
結論として、アクセス修飾子はコンパイラーには必要ありませんが、それらは主に、優れた実践のために非常に優れていると考えられています。このようなガイドラインでは、コンパイラが必要としない場合でも、開発者は入念なアクセス制御を実行する必要があります。
なぜいくつかの現代の言語は
protected
を削除するのですか?
「それが言語設計者が行うことを決めたので」を除いて、その質問に普遍的に適用できる答えはありません。
いいえ、それは必須ではありません: Bjarne Stroustrup、 explained 単純にprotected
をC++リリース1.2に追加して、クラス開発者に便利な機能を提供することを考え、たった5年後、それは厄介なバグの原因であり、幸いにも誰も使用せざるを得なかったと結論付けています。最近では、彼は 使用しないことをお勧めします です。
protected
に対する実用的な引数は、より強力なカプセル化の利点であり、 最小の知識の原則 :
public
であり、誰でも使用できます。private
であり、外部アクセスから保護する必要があります。protected
メンバー(それ以外の場合はパブリック)は、内部者(派生クラスの開発者)が他のユーザーと同様に誤用する可能性があります。正式な引数実務経験を確認します。これは Liskov置換の原則 と関係があり、より正確にはその履歴規則です。
ユーザーは、オブジェクトの「見かけの」タイプについてのみ知っていれば十分だと思います。サブタイプは、スーパータイプについて証明できるすべてのプロパティを保持する必要があります。
-Barbara Liskov&Jeanette Wing in 動作のサブタイピングの概念
引用された記事の詳細に触れずに、保護されたメンバーは、派生クラス(サブタイプ)がそのパブリックオペレーションに依存することなく、予期しない方法で基本クラスオブジェクト(スーパータイプ)の状態を変更することを許可します。
appearances および誤った約束に注意してください。 Swift private
は、他の言語ではprivate
とprotected
の間にあります:
プライベートアクセスでは、エンティティの使用を囲んでいる宣言、、および同じファイル内のその宣言の拡張に制限します。 (...).
-アップル、Swiftプログラミング言語
Pythonは、オブジェクト指向プログラミングのアプローチを強く支持する言語でもあります。クラスとオブジェクトの古典的なアプローチを使用します。
ただし、覚えておくべきことは、「Word」はあなたと(将来の)メンテナの間の契約にすぎないということです。何かに別の名前を付けたり、明示的ではない名前を付けたりしても、この契約が存在しないという意味ではありません。
Pythonは「私たちはすべて大人なのです」という信条を使用しており、人々はオブジェクトに反対するのではなく、オブジェクトを操作することを期待しています。したがって、すべてをパブリックと見なし、クラスを記述することによって独自の契約を結ぶことが期待されます。 (設計書PEP8は、接頭辞が_
は、IDEがこれを理解しているプライベートフィールドの契約を示すことをお勧めします)。
保護されている(それから派生する場合を除いて、変数に直接アクセスできないという考えとして)とにかく弱いコントラクトです。重要なフィールドへの誤った変更によるミスを「防止」したい場合、内部状態を保護するために、保護された変数は引き続き自由に変更でき、派生クラスはこれを簡単に公開して悪影響を及ぼす可能性があります。
したがって、質問はあなたにあるはずです:直接有利な使用なしに言語に「なぜ追加のパラダイムを追加するのか」? YAGNIもここに適用されます。
保護されたアクセス修飾子をすべての一般的なOO=言語から削除する必要があることを決定する前に、それを失うことはかなり不便であることを指摘したいと思います。
多くの派生クラスの青写真として機能する抽象基本クラスでは、これらの派生物のエンドユーザーにとって意味のない、これらの派生物の多くのサポートメソッドが存在する可能性があります。エルゴ、あなたは騒々しいインターフェースを手に入れるでしょう、そしてあなたはこれらのメソッドがオブジェクトクライアントによって呼び出されるべきではないことを知らせる別の方法を見つける必要があるでしょう。
それを回避する方法があると言う人もいます。代わりに構成を適用できます。そもそも継承を使わない理由はたくさんあります。これらのステートメントにどのようなメリットがある場合でも、保護は継承の適用をサポートするために存在します。保護されていない有用な抽象クラスを書くのは難しいでしょう。
抽象基本クラス以外ではあまり使用しないと言えます。しかし、抽象基本クラスがある限り、保護されたキーワードを維持したいと思います。ありがとうございます。
最初のオブジェクト指向言語の1つであるSmalltalkにはprotected
キーワードまたはメカニズムがありません。また、private
も明示的ではありませんが、インスタンス変数に対して暗黙的に指定されており、メソッドの慣例により示唆されています。可鍛性を大きなハンマーですべてを打つための招待状と見なさない限り、うまく機能します:-)
protected
は、データのアクセス制御に関するものです。 OOPはカプセル化に関するものです。
OOP=の主な目的は、エンティティ(データとその操作)が互いに弱く結合されるようにコードを構造化することです。カプセル化されたデータが(アクセスに対して)制御されるという事実または保護は継承とより密接に関連しています。一般化/特殊化の関係を実現するための技術の1つです。しかし、継承でさえ必要ない場合は、委任を使用してG/Sをより微妙に実装することができます。保護されているケースは使用されません。
Flaterが書いたように、アクセス制限は厳密には必要ありません。
また、保護されたアクセスが一度に複数のことを実行しようとしていると主張する人もいます。あなたは次のような場合に保護を使用できます:
より良い修飾子(Java ish構文):
そして、短くて明確にするために、3つの異なる単語を使用します。
Swiftと明示的に言及したので、Swiftにprotected
がない理由についてお答えします。
他の多くの言語とは異なり、Swiftを使用すると、所有していないものでも、他のタイプ(クラス、構造体、列挙型、プロトコルなど)に「拡張機能」を書き込むことができます。このような拡張機能により、ライブラリAのタイプをライブラリBのプロトコル(「遡及的モデリング」の例)に準拠させるには、たとえば、ORMのプロトコルに準拠したいImage
オブジェクト(ライブラリAから)があるとしますDatabaseSerializable
(ライブラリBから)。これにより、データベースにシリアル化できます。ほとんどの言語では、すべてをラップアップする必要があります adapters すべての場所にあります。Swiftでは、Image
に直接準拠してDatabaseSerializable
extension Image: DatabaseSerializable {
func serailize(to db: Database) {
// do whatever is necessary to save to the db or whatever
}
これらはSwiftで行われるプログラミングのスタイルに大きな影響を与える非常に中心的な機能です。たとえば、複数のプロトコルへの適合を視覚的に区別するためによく使用されます。次に例を示します。
class Person {
let firstName: String
let lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.firstName = lastName
}
}
// This impl can be auto-synthesized by the compiler, but I'm showing it here as an example anyway
extension Person: Equatable {
static func == (lhs: Person, rhs: Person) -> Bool {
return lhs.firstName == rhs.firstName && lhs.lastName == rhs.lastName
}
}
// This impl can be auto-synthesized by the compiler, but I'm showing it here as an example anyway
extension Person: Hashable {
func hash(into hasher: inout Hasher) {
hasher.combine(self.firstName)
hasher.combine(self.lastName)
}
}
extension Person: CustomStringConvertible {
var description: String { "\(firstName) \(lastName)" }
}
この例では、保護フィールドsocialInsuranceNumber
があったとします。他のクラスのコンテキストにいる場合は、アクセスできません。 Person
クラスまたはサブクラスにいる場合は、アクセスできるはずです。しかし、Person
拡張機能を使用している場合はどうなりますか?拡張が行われる場所に依存する必要がありますか? (たとえば、Person
と同じモジュールで許可しますが、他のモジュールの拡張機能からのアクセスは許可しません)。これを行うとどうなりますか?
extension Person {
public var publicSocialInsuranceNumber: SIN {
self.socialInsuranceNumber // this should be protected!
}
}
私はprotected
アクセスレベルが提供する保護を簡単に回避しました。
代わりに、Swiftにはfileprivate
があり、定義ファイルからフィールドにアクセスできることを除いて、private
のように機能します。したがって、Person
Person.Swift
はsocialInsuranceNumber
にアクセスできますが、他の場所で定義されたPerson
拡張機能はアクセスできません。
Swiftでは、サブクラスは基本クラスとあまり関係がないと判断されました。一部の情報が公開されていない場合、サブクラスでは利用できません。
メンバーがファイル内でのみ使用できるようにする「fileprivate」もあるので、クラスが強く関連している場合、それらを1つのファイルに実装できます。