简体   繁体   中英

Scala and encapsulation?

Since I started to study OOP encapsulation was always something that raised questions to me. Getters and setters in 99% of the cases seemed like a big lie: what does it matter to have setter if it changes the reference of the private field, and getter that returns reference to mutable object? Of course there are many things that make life easier with getters and setters pattern (like Hibernate that creates proxies on entities). In Scala there is some kind of solution: don't lie to yourself, if your field is val you have nothing to fear of and just make it public. Still this doesn't solve the question of methods, should I ever declare a method private in Scala? Why would I declare a method private in Java? Mostly if it's a helper method and I don't want to pollute my class namespace, and if the method changes our internal state. The second issue doesn't apply (mostly & hopefully) to Scala, and the first one could be simply solved with appropriate traits. So when would I want to declare a method private in Scala? What is the convention for encapsulation in Scala? I would highly appreciate if you help me to order my thoughts on subject.

Getters and setters (or accessor/mutator methods) are used to encapsulate data, which is commonly considered one of the tenets of OOP.

They exist so that the underlying implementation of an object can change without compromising client code, as long as the interface contract remains unchanged.
This is a principle aiming to simplify maintenance and evolution of the codebase.

Even Scala has encapsulation, but it supports the Uniform Access Principle by avoiding explicit use of get/set (a JavaBean convention) by automatically creating accessor/mutator methods that mimics the attribute name (eg for a public val name attribute a corresponding def name public accessor is generated and for a var name you also have the def name_= mutator method).

For example if you define

class Encapsulation(hidden: Any, val readable: Any, var settable: Any)

the compiled .class is as follows

C:\devel\scala_code\stackoverflow>javap -cp . Encapsulation
Compiled from "encapsulation.scala"
public class Encapsulation {
  public java.lang.Object readable();
  public java.lang.Object settable();
  public void settable_$eq(java.lang.Object);
  public Encapsulation(java.lang.Object, java.lang.Object, java.lang.Object)
}

Scala is simply designed to avoid boilerplate by removing the necessity to define such methods.

Encapsulation (or information hiding) was not invented to support Hibernate or other frameworks. In fact in Hibernate you should be able to annotate the attribute field directly, all the while effectively breaking encapsulation.


As for the usefulness of private methods, it's once again a good design principle that leads to DRY code (if you have more than one method sharing a piece of logic), to better focusing the responsibility of each method, and to enable different composition of the same pieces.

This should be a general guideline for every method you define, and only a part of the encapsulated logic would come out at the public interface layer, leaving you with the rest being implemented as private (or even local) methods.

In scala (as in java) private constructors also allows you to restrict the way an object is instantiated through the use of factory methods.

Encapsulation is not only a matter of getter/setter methods or public/private accessor modifiers. That's a common misconception amongst Java developer who had to spend to much time with Hibernate (or similar JavaBean Specification based libraries).

In object-oriented programming, encapsulation not only refers to information hiding but it also refers to bundling both the data and the methods (operating on that data) together in the same object.

To achieve good encapsulation, there must a clear distinction between the those methods you wish to expose to the public (the so called public interface ) and the internal state of an object which must comply with its data invariants.

In Scala the are many ways to achieve object-oriented encapulation. For example, one of my preferred is:

trait AnInterface {
  def aMethod(): AType
}

object AnInterface {
  def apply() = new AnHiddenImplementation()

  private class AnHiddenImplementation {
    var aVariable: AType = _
    def aMethod(): AType = {
       // operate on the internal aVariable
    }
  }
}

Firstly, define the trait (the public interface ) so to make immediately clear what the clients will see. Then write its companion object to provide a factory method which instantiate a default concrete implementation. That implementation can be completely hidden from clients if defined private inside the companion object.

As you can see the Scala code is much more concise of any Java solution

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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