简体   繁体   English

为什么使用封装进行数据隐藏?

[英]why use encapsulation for data hiding?

Here is a very simple code example: 这是一个非常简单的代码示例:

public class Name {

    public string Name = "John"

}

If you made an instance of the Name class in another class( Name n = Name(); ), you can change the Name string of the instance by doing n.Name = "Chris"; 如果您在另一个类( Name n = Name(); )中创建了Name类的实例,则可以通过执行n.Name = "Chris";来更改实例的Name字符串n.Name = "Chris"; . However, this will just change the name for the instance of the Name class not the class itself. 但是,这只会更改Name类实例的名称,而不是类本身。 If you made a new instance of the Name class( Name na = Name(); ), and called the Name variable, it will still be "John" not "Chris". 如果创建了Name类的新实例( Name na = Name(); ),并将其命名为Name变量,则它仍然是“ John”而不是“ Chris”。

I know that one of the reasons encapsulation is used is that so the user can't modify variables in a class. 我知道使用封装的原因之一是用户无法修改类中的变量。 In the above example I showed, the user is not modifying the variable of the class itself. 在上面显示的示例中,用户没有修改类本身的变量。

Let's say you load a data from a server or local database and you parsed it to your own object. 假设您是从服务器或本地数据库加载数据并将其解析为自己的对象。 For example, user's profile. 例如,用户的个人资料。

class User {

    public static final String TAG = User.class.getSimpleName();
    private String name; 
    private String userToken; 
}

The User class doesn't have any default or static values because the data will be fed by the server. User类没有任何默认值或静态值,因为数据将由服务器提供。

The data you get from the server or DB will be something like, 您从服务器或数据库获取的数据将类似于:

name: John
userToken: 0x23fa8.... 

If the fields in your User class is public, the user (or any other parties) will access to the data and manipulate it. 如果您的User类中的字段是公共的,则用户(或任何其他方)将访问数据并对其进行操作。 Think about the case the user changed his/her AuthToken, device id, or backup key phrases, etc... and if this manipulated data gets committed, it will cause a lot of trouble and clearly not what you want. 考虑一下用户更改其AuthToken,设备ID或备份关键字短语等的情况,如果提交了这种可操纵的数据,将会造成很多麻烦,并且显然不是您想要的。

Just like @StalematOfTuning said, encapsulation is about protecting object instance's fields, not protecting pre-defined, class variables. 就像@StalematOfTuning所说的那样,封装是关于保护对象实例的字段的,而不是保护预定义的类变量。 If you only want to create a class with pre-defined class properties (static values), there's really no point of constructing a class because the class you created cannot be reusable for any other situation. 如果您只想创建具有预定义类属性(静态值)的类,则实际上没有必要构造一个类,因为您创建的类无法在任何其他情况下重用。

For example, if you already know the Name class will have one and only value, "John", it is wasteful to even constructing a class. 例如,如果您已经知道Name类将只有一个值“ John”,那么即使构造一个类也是浪费的。 The whole point of creating a class or an object is to reuse it for any possible input or data. 创建类或对象的全部目的是将其重用于任何可能的输入或数据。

OK so I think you are confused about a few things here. 好的,我想您对这里的一些事情感到困惑。

For starters, let's clean up this example a bit so it's easier to follow. 首先,让我们整理一下这个示例,以便更轻松地理解。 I'm going to make a Person class: 我要上一个Person类:

public class Person {
    public String name;
}

Doesn't get much simpler than that. 没有比这更简单的了。 So, right now I have defined the class Person but it's important to note that no Person objects actually exist yet. 因此,现在我已经定义了Person类,但需要注意的是,实际上还没有Person对象存在。 The class Person can be thought of as a blueprint for making Person objects. 可以将class Person视为制作Person对象的蓝图。 So, let's make a person: 所以,让我们做一个人:

public static void main(String[] args) {
    Person john = new Person();
    john.name = "John";
}

Here I have initialized a Person by calling its default constructor (note the word new ), and set its name property to "John". 在这里,我通过调用其默认构造函数(请注意单词new )来初始化一个Person ,并将其name属性设置为“ John”。

Now, if someone else was using this code they could come along and say hey let's change his name because why not? 现在,如果其他人正在使用此代码,他们可能会说,嘿,让我们更改他的名字,因为为什么不呢?

john.name = "Bob";

But wait... a person's name changing might not make sense in the context of our program. 但是,等等...在我们的程序中,更改人名可能没有意义。 Perhaps we don't want people to be able to change their names whenever they want as it might cause us problems later down the line. 也许我们不希望人们能够随时随地更改其姓名,因为这可能会在以后给我们带来麻烦。 Even worse, someone could try and do something weird like turning his name into a number or any other crazy garbage that completely breaks the program. 更糟糕的是,有人可能会尝试做一些奇怪的事情,例如将其名字变成数字或任何其他完全破坏程序的疯狂垃圾。 We can fix this by instead encapsulating the name property. 我们可以通过封装name属性来解决此问题。 We do this by making name a private field and define a constructor as well as a way to view that field. 为此,我们将name设为private字段,并定义构造函数以及查看该字段的方法。

public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() { 
        return name;
    }
}

Now, we must define what we want name to be when we create the object. 现在,我们必须定义创建对象时要使用的name Let's make John again: 让我们再次使约翰:

Person john = new Person("John");

But most importantly, there is no way to change John's name anymore . 但最重要的是, 再也没有办法更改约翰的名字了 Since we have not provided a public way to do so, it is impossible. 由于我们没有提供public方式来这样做,所以这是不可能的。

If we try to change john now, our program will not compile: 如果我们现在尝试更改john,我们的程序将无法编译:

john.name = "Bob"; //NOT ALLOWED

And to view our name , we will instead have to use our getter method: 要查看我们的name ,我们将不得不使用我们的getter方法:

System.out.println(john.getName()); //prints "John"

This is the essence of encapsulation: protecting an object's properties from undesired changes in state. 这就是封装的本质:保护对象的属性免受状态的意外更改。 Instead of just handing over our data, we hide its actual state and only allow actions on it through controlled channels. 我们不仅隐藏数据,还隐藏它的实际状态,只允许通过受控通道对其进行操作。 If we did want to allow names to be changed, maybe we are ok with it but only if the new name meets certain criteria. 如果我们确实希望更改名称,那么也许可以接受,但前提是新名称符合特定条件。 Let's say: the new name has to be at least 3 characters long (sorry Al). 假设:新名称必须至少包含3个字符(抱歉,Al)。 We can provide a setter like so: 我们可以像这样提供设置器:

public void setName(String newName) {
    if(newName.length() < 3) {
        System.out.println("name is too short!");
    } else {
        name = newName;
    }
}

In this way we can have much greater control over how our data is used. 这样,我们可以更好地控制数据的使用方式。 I hope this helps clear up your confusion a bit. 我希望这可以帮助您消除一些困惑。

Also note that if you create a new Person you will again need to specify another String to use to set its name in the constructor. 还要注意,如果创建一个新的Person ,则将再次需要指定另一个String来在构造函数中设置其name Every object must be uniquely initialized. 每个对象都必须进行唯一初始化。

why use encapsulation for data hiding? 为什么使用封装进行数据隐藏?

A major advantage of modular code development is that modules can be uncoupled from each other. 模块化代码开发的主要优点是模块可以彼此分离。 Modules that are not coupled have no dependencies upon each other. 未耦合的模块彼此之间没有依赖性。 This means that they may be developed independently and in parallel. 这意味着它们可以独立和并行开发。 Moreover, as long as the contract that governs the behavior of modules does not change, then modules that are uncoupled can be modified or rewritten at any time without breaking the system in which they operate (as long as the behavior of the module continues to conform to its contract with other modules, ie its API). 此外,只要支配模块行为的合同不变,那么可以随时修改或重写未耦合的模块,而不会破坏其运行的系统(只要模块的行为继续保持一致)与其其他模块(即其API)的合同)。

Consider something like: 考虑类似:

public class A {
    public String name = "Hello";
        :
        :
}

public class B {
    private A myA = new A();
    myA.name = "Hello World!"
        :
        :
}

We have written B to be dependent upon the way in which A has been implemented: namely that A has an instance field named name . 我们写B依赖于实现A的方式:即A具有一个名为name的实例字段。 The modules A and B are now coupled to each other because B depends upon A having this instance field. 现在模块A和B彼此耦合,因为B依赖于具有此实例字段的A。 If we ever decide to change the way that A is implemented, we may be in trouble because a major overhaul to A may require a major overhaul in B. 如果我们决定改变A的实现方式,则可能会遇到麻烦,因为对A进行大修可能需要对B进行大修。

Data hiding is valuable because it avoids this problem. 数据隐藏很有价值,因为它避免了这个问题。 We want to write B in a way such that B depends only on the behavior of A (ie, it's API) and not at all on how that behavior is implemented. 我们要以这样一种方式编写B,即B仅取决于A的行为(即,它是API),而不完全取决于该行为的实现方式。 That A has an instance field name is an implementation detail and we want to keep those details separated from the rest of the software system. A具有实例字段name是实现细节,我们希望将那些细节与软件系统的其余部分分开。 It is usually true that any mutable fields that comprise the state of your objects should be hidden from other parts of the system. 通常,构成对象状态的任何可变字段都应该对系统的其他部分隐藏。 They are implementation details that should not be made a part of the API that modules export to the rest of the system. 它们是实现细节,不应将其作为模块导出到系统其余部分的API的一部分。

TL;DR: Generally speaking, the more uncoupled modules are, the easier and cheaper they are to develop and to maintain. TL; DR:通常来说,未耦合的模块越多,它们的开发和维护就越容易且便宜。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM