web-dev-qa-db-ja.com

OOPでは、「保護された」キーワードは必須ではありませんか?

一部の最新言語(Swift、Dartなど)は、protectedアクセス修飾子キーワードをサポートしていません。 Swiftはプロトコル指向の言語ですが、Dartは完全にオブジェクト指向の言語であると聞きました。

これらの現代の言語がprotectedをサポートしないのはなぜですか?完全なオブジェクト指向プログラミングにはprivatepublicだけが必要ですか?

親クラスから子クラスに渡したいデータやインターフェースがいくつかある場合、protectedアクセス修飾子キーワードを用意すると便利だと思います。なぜいくつかの現代の言語はprotectedをサポートしないのですか?

28
ShutUpILoveYou

それはあなたが「必須」で何を意味するかによります。

アクセス修飾子は必須ではありません。すべてのアクセス修飾子をpublicで置き換えることができます。ほとんどのアプリケーションは、さまざまなアクセス修飾子を使用したときと同じように機能し、コンパイラの主な目的(動作するアプリケーションの出力)がアクセス修飾子に直接依存しないことを証明します。 。

Deliothがコメントで述べたように、JavascriptとPythonは、OOPの機能はまだありますが、アクセス修飾子の概念はありません。 OOPアクセス修飾子は必要ありません。

ただし、間違いを避けたい場合は、開発者の観点から修飾子にアクセスすることが非常に重要です。アクセス制限の欠如は、開発者がすべきではない依存関係に直接アクセスすることにつながり(たとえば、検証/承認レイヤーを回避する)、これはバグにつながり、時間と労力が費やされます。

結論として、アクセス修飾子はコンパイラーには必要ありませんが、それらは主に、優れた実践のために非常に優れていると考えられています。このようなガイドラインでは、コンパイラが必要としない場合でも、開発者は入念なアクセス制御を実行する必要があります。

なぜいくつかの現代の言語はprotectedを削除するのですか?

「それが言語設計者が行うことを決めたので」を除いて、その質問に普遍的に適用できる答えはありません。

45
Flater

いいえ、それは必須ではありません: Bjarne Stroustrup、 explained 単純にprotectedをC++リリース1.2に追加して、クラス開発者に便利な機能を提供することを考え、たった5年後、それは厄介なバグの原因であり、幸いにも誰も使用せざるを得なかったと結論付けています。最近では、彼は 使用しないことをお勧めします です。

protectedに対する実用的な引数は、より強力なカプセル化の利点であり、 最小の知識の原則

  • メンバーはpublicであり、誰でも使用できます。
  • または、メンバーはprivateであり、外部アクセスから保護する必要があります。
  • 注意深く使用する必要があるprotectedメンバー(それ以外の場合はパブリック)は、内部者(派生クラスの開発者)が他のユーザーと同様に誤用する可能性があります。

正式な引数実務経験を確認します。これは Liskov置換の原則 と関係があり、より正確にはその履歴規則です。

ユーザーは、オブジェクトの「見かけの」タイプについてのみ知っていれば十分だと思います。サブタイプは、スーパータイプについて証明できるすべてのプロパティを保持する必要があります。
-Barbara Liskov&Jeanette Wing in 動作のサブタイピングの概念

引用された記事の詳細に触れずに、保護されたメンバーは、派生クラス(サブタイプ)がそのパブリックオペレーションに依存することなく、予期しない方法で基本クラスオブジェクト(スーパータイプ)の状態を変更することを許可します。

appearances および誤った約束に注意してください。 Swift privateは、他の言語ではprivateprotectedの間にあります:

プライベートアクセスでは、エンティティの使用を囲んでいる宣言、、および同じファイル内のその宣言の拡張に制限します。 (...).
-アップル、Swiftプログラミング言語

33
Christophe

Pythonは、オブジェクト指向プログラミングのアプローチを強く支持する言語でもあります。クラスとオブジェクトの古典的なアプローチを使用します。

ただし、覚えておくべきことは、「Word」はあなたと(将来の)メンテナの間の契約にすぎないということです。何かに別の名前を付けたり、明示的ではない名前を付けたりしても、この契約が存在しないという意味ではありません。

Pythonは「私たちはすべて大人なのです」という信条を使用しており、人々はオブジェクトに反対するのではなく、オブジェクトを操作することを期待しています。したがって、すべてをパブリックと見なし、クラスを記述することによって独自の契約を結ぶことが期待されます。 (設計書PEP8は、接頭辞が_は、IDEがこれを理解しているプラ​​イベートフィールドの契約を示すことをお勧めします)。

保護されている(それから派生する場合を除いて、変数に直接アクセスできないという考えとして)とにかく弱いコントラクトです。重要なフィールドへの誤った変更によるミスを「防止」したい場合、内部状態を保護するために、保護された変数は引き続き自由に変更でき、派生クラスはこれを簡単に公開して悪影響を及ぼす可能性があります。

したがって、質問はあなたにあるはずです:直接有利な使用なしに言語に「なぜ追加のパラダイムを追加するのか」? YAGNIもここに適用されます。

9
paul23

保護されたアクセス修飾子をすべての一般的なOO=言語から削除する必要があることを決定する前に、それを失うことはかなり不便であることを指摘したいと思います。

多くの派生クラスの青写真として機能する抽象基本クラスでは、これらの派生物のエンドユーザーにとって意味のない、これらの派生物の多くのサポートメソッドが存在する可能性があります。エルゴ、あなたは騒々しいインターフェースを手に入れるでしょう、そしてあなたはこれらのメソッドがオブジェクトクライアントによって呼び出されるべきではないことを知らせる別の方法を見つける必要があるでしょう。

それを回避する方法があると言う人もいます。代わりに構成を適用できます。そもそも継承を使わない理由はたくさんあります。これらのステートメントにどのようなメリットがある場合でも、保護は継承の適用をサポートするために存在します。保護されていない有用な抽象クラスを書くのは難しいでしょう。

抽象基本クラス以外ではあまり使用しないと言えます。しかし、抽象基本クラスがある限り、保護されたキーワードを維持したいと思います。ありがとうございます。

8
Martin Maat

最初のオブジェクト指向言語の1つであるSmalltalkにはprotectedキーワードまたはメカニズムがありません。また、privateも明示的ではありませんが、インスタンス変数に対して暗黙的に指定されており、メソッドの慣例により示唆されています。可鍛性を大きなハンマーですべてを打つための招待状と見なさない限り、うまく機能します:-)

3

protectedは、データのアクセス制御に関するものです。 OOPはカプセル化に関するものです。

OOP=の主な目的は、エンティティ(データとその操作)が互いに弱く結合されるようにコードを構造化することです。カプセル化されたデータが(アクセスに対して)制御されるという事実または保護は継承とより密接に関連しています。一般化/特殊化の関係を実現するための技術の1つです。しかし、継承でさえ必要ない場合は、委任を使用してG/Sをより微妙に実装することができます。保護されているケースは使用されません。

Flaterが書いたように、アクセス制限は厳密には必要ありません。

また、保護されたアクセスが一度に複数のことを実行しようとしていると主張する人もいます。あなたは次のような場合に保護を使用できます:

  1. メソッドはサブクラスメソッドによって呼び出される必要があります
  2. メソッドはサブクラスメソッドによって実装され、スーパークラスまたは他のサブクラスによって呼び出されます
  3. サブクラスによってovveridenでき、スーパークラスまたは他のサブクラスによって呼び出されるメソッド
  4. フィールドと同様のもの

より良い修飾子(Java ish構文):

  1. 保護された最終
  2. 2つのメソッドに分割されます。1つは保護された(サブクラスが呼び出さない場合はプライベート)ファイナル(呼び出し)で、もう1つはサブクラスが実装する必要がある保護された抽象ですが、呼び出しません。
  3. 2.と同じですが、要約はありません

そして、短くて明確にするために、3つの異なる単語を使用します。

0
user470365

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のように機能します。したがって、PersonPerson.SwiftsocialInsuranceNumberにアクセスできますが、他の場所で定義されたPerson拡張機能はアクセスできません。

Swiftでは、サブクラスは基本クラスとあまり関係がないと判断されました。一部の情報が公開されていない場合、サブクラスでは利用できません。

メンバーがファイル内でのみ使用できるようにする「fileprivate」もあるので、クラスが強く関連している場合、それらを1つのファイルに実装できます。

0
gnasher729