开发者

Encapsulation. Well-designed class

开发者 https://www.devze.com 2023-01-21 04:48 出处:网络
Today I read a book and the author wrote that in a well-designed class the only way to access attributes is through one of that class methods. Is it a widely accepted thought? Why is it so important t

Today I read a book and the author wrote that in a well-designed class the only way to access attributes is through one of that class methods. Is it a widely accepted thought? Why is it so important to encapsulate the attributes? What could be the consequences of not doing it? I read somewhere earlier that this improves security or something like that. Any example in PHP开发者_运维技巧 or Java would be very helpful.


Is it a widely accepted thought?

In the object-oriented world, yes.

Why is it so important to encapsulate the attributes? What could be the consequences of not doing it?

Objects are intended to be cohesive entities containing data and behavior that other objects can access in a controlled way through a public interface. If an class does not encapsulate its data and behavior, it no longer has control over the data being accessed and cannot fulfill its contracts with other objects implied by the public interface.

One of the big problems with this is that if a class has to change internally, the public interface shouldn't have to change. That way it doesn't break any code and other classes can continue using it as before.

Any example in PHP or Java would be very helpful.

Here's a Java example:

public class MyClass {
    // Should not be < 0
    public int importantValue;
    ...
    public void setImportantValue(int newValue) {
        if (newValue < 0) {
           throw new IllegalArgumentException("value cannot be < 0");
        }
    }
    ...
}

The problem here is that because I haven't encapsulated importantValue by making it private rather than public, anyone can come along and circumvent the check I put in the setter to prevent the object from having an invalid state. importantValue should never be less than 0, but the lack of encapsulation makes it impossible to prevent it from being so.


What could be the consequences of not doing it?

The whole idea behind encapsulation is that all knowledge of anything related to the class (other than its interface) is within the class itself. For example, allowing direct access to attributes puts the onus of making sure any assignments are valid on the code doing the assigning. If the definition of what's valid changes, you have to go through and audit everything using the class to make sure they conform. Encapsulating the rule in a "setter" method means you only have to change it in one place, and any caller trying anything funny can get an exception thrown at it in return. There are lots of other things you might want to do when an attribute changes, and a setter is the place to do it.

Whether or not allowing direct access for attributes that don't have any rules to bind them (e.g., anything that fits in an integer is okay) is good practice is debatable. I suppose that using getters and setters is a good idea for the sake of consistency, i.e., you always know that you can call setFoo() to alter the foo attribute without having to look up whether or not you can do it directly. They also allow you to future-proof your class so that if you have additional code to execute, the place to put it is already there.

Personally, I think having to use getters and setters is clumsy-looking. I'd much rather write x.foo = 34 than x.setFoo(34) and look forward to the day when some language comes up with the equivalent of database triggers for members that allow you to define code that fires before, after or instead of a assignments.


Opinions on how "good OOD" is achieved are dime a dozen, and also very experienced programmers and designers tend to disagree about design choices and philosophies. This could be a flame-war starter, if you ask people across a wide varieties of language background and paradigms.

And yes, in theory are theory and practice the same, so language choice shouldn't influence high level design very much. But in practice they do, and good and bad things happen because of that.

Let me add this: It depends. Encapsulation (in a supporting language) gives you some control over how you classes are used, so you can tell people: this is the API, and you have to use this. In other languages (e.g. python) the difference between official API and informal (subject to change) interfaces is by naming convention only (after all, we're all consenting adults here)

Encapsulation is not a security feature.


Another thought to ponder

Encapsulation with accessors also provides much better maintainability in the future. In Feanor's answer above, it works great to enforce security checks (assuming your instvar is private), but it can have much further reaching benifits.

Consider the following scenario:
1) you complete your application, and distribute it to some set of users (internal, external, whatever).
2) BigCustomerA approaches your team and wants an audit trail added to the product.

If everyone is using the accessor methods in their code, this becomes almost trivial to implement. Something like so:

MyAPI Version 1.0

public class MyClass {
    private int importantValue;
    ...
    public void setImportantValue(int newValue) {
        if (newValue < 0) {
           throw new IllegalArgumentException("value cannot be < 0");
        }
        importantValue = newValue;
    }
    ...
}



MyAPI V1.1 (now with audit trails)

public class MyClass {
    private int importantValue;
    ...
    public void setImportantValue(int newValue) {
        if (newValue < 0) {
           throw new IllegalArgumentException("value cannot be < 0");
        }
        this.addAuditTrail("importantValue", importantValue, newValue);
        importantValue = newValue;
    }
    ...
}

Existing users of the API make no changes to their code and the new feature (audit trail) is now available.
Without encapsulation using accessors your faced with a huge migration effort.


When coding for the first time, it will seem like a lot of work. Its much faster to type: class.varName = something vs class.setVarName(something); but if everyone took the easy way out, getting paid for BigCustomerA's feature request would be a huge effort.


In Object Oriente Programming there is a principle that is known as (http://en.wikipedia.org/wiki/Open/closed_principle): POC --> Principle of Open and Closed. This principle stays for: a well class design should be opened for extensibility (inheritance) but closed for modification of internal members (encapsulation). It means that you could not be able to modify the state of an object without taking care about it.

So, new languages only modify internal variables (fields) through properties (getters and setters methods in C++ or Java). In C# properties compile to methods in MSIL.

C#:

int _myproperty = 0;
public int MyProperty
{
    get { return _myproperty; }
    set { if (_someVarieble = someConstantValue) { _myproperty = value; } else { _myproperty = _someOtherValue; }  }    
}

C++/Java:

int _myproperty = 0;
public void setMyProperty(int value) 
{
  if (value = someConstantValue) { _myproperty = value; } else { _myproperty = _someOtherValue; }
}
public int getMyProperty() 
{
    return _myproperty;
}


Take theses ideas (from Head First C#):

  • Think about ways the fields can misused. What can go wrong if they're not set properly.
  • Is everything in your class public? Spend some time thinking about encapsulation.
  • What fields require processing or calculation? They are prime candidates.
  • Only make fields and methods public if you need to. If you don't have a reason to declare something public, don't.
0

精彩评论

暂无评论...
验证码 换一张
取 消