Private Names and Methods

Kevin Smith khs4473 at gmail.com
Wed Apr 4 18:27:38 PDT 2012


Let's say that we have a class with two methods, and we divide the body of
each method into abstract "regions":

    M1() {
      <A>
      <B>
      <C>
    }

    M2() {
      <D>
      <C>
      <E>
    }

Both M1 and M2 share the region <C>.  In a traditional class-based
language, we would refactor <C> into an abstract operation using a private
method:

    M1() {
      <A>
      <B>
      this.M3();
    }

    M2() {
      <D>
      this.M3();
      <E>
    }

    private M3() {
      <C>
    }

In a traditional class-based language, the refactored code will:

1) provide an identical public interface, and
2) be functionally identical

These two guarantees allow us to separate interface from implementation,
and to localize the implementation strategy.

Will private methods, implemented with private name objects, provide us
with the same guarantees?  Let's refactor again, this time using a private
name for the new method:

    // Assume that there exists a "const M3 = Name.create();" somewhere
above

    M1() {
      <A>
      <B>
      this[M3]();
    }

    M2() {
      <D>
      this[M3]();
      <E>
    }

   [M3]() {
      <C>
    }

Since M3 is not visible to any code that does not also have a reference to
the M3 private name, we can say that this refactoring provides the same
public interface.  But is it functionally identical?

Let's assume that prior to the refactoring, M1 and M2 were generic methods.
 That is, they reference only properties of |this| which correspond to some
subset S of the public interface of the class.  Any other object which
implements S can also be used with M1 and M2 (like the generic methods
defined on Array.prototype).

After the refactoring shown above, there's no possibility that M1 and M2
can be generic, since they access a privately named property of |this|
which by definition is not a part of the public interface of the class.  So
in this sense, the refactored code is not functionally identical to the
original code.

If we wanted to preserve identical functionality, we could define M3 as a
regular function and call it using the call method of Function.prototype:

    // Somewhere *before* the class definition we have:
    function M3() {
      <C>
    }

    ...

    M1() {
      <A>
      <B>
      M3.call(this);
    }

    M2() {
      <D>
      M3.call(this);
      <E>
    }

The above strategy works, but (a) explicitly calling M3 via call() is
awkward, and (b) it defeats the purpose of an elegant class syntax if we
must refactor code into functions outside of the class definition.

Thoughts?

kevin
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20120404/a95f6a02/attachment.html>


More information about the es-discuss mailing list