0

In OOP languages, programs can define static methods that can generate objects with different parameters (including subclassed or pre-generated objects), but they are not as commonly used because we also have constructors that can take parameters, even though constructors are forced to synchronously return a single instance of that object immediately. So, what if there was a language that only permitted private constructors, and if you wished to create an object externally, it was through generators?

For instance, a class might be defined like this:

public class Foo {
    private int param;
    public void doSomething() { ... }
    public void doSomethingWith(Foo other) { ... }

    public static Foo make(int param){
        Foo self = CONSTRUCT();
        self.param = param;
        return self;
    }
}

and another class would request it like this:

Foo whatevsOne = Foo.make(1);

but could not do this:

// ERR: Cannot construct Foo from outside Foo
Foo whatevsTwo = Foo.CONSTRUCT();

If the class was a subclass, CONSTRUCT() would become CONSTRUCT_FROM() and take an argument of the object to make the base object - rather than allocating a new spot in heap memory, CONSTRUCT_FROM() will define the new object as an extension to it:

public class FooOne extends Foo {
    public static FooOne make(){
        return CONSTRUCT_FROM(FooOne.make(1));
    }
}

This would probably come with some kind of marker placed on methods that return objects so as to guarantee that the object did not exist before the start of that method (and could thus not be double-overlaid and would definitely exist), perhaps something like public static new Foo make().

With this in place, one thing I can see as a benefit is that constructors of objects can now follow all of the rules of other methods, following good naming patterns and programming conventions that overcome the limitations of constructors, yet, through CONSTRUCT_FROM(base), allow inheritance. For instance, I can create an object asynchronously:

public static Future<new Foo> getFromInternet(String url){
    Future<String> future = GetDataFromInternet(url);
    return future.then((data) -> {
        Foo foo = CONSTRUCT();
        /* fill foo using data */
        return foo;
    });
}

and still subclass it:

public static Future<new SubFoo> getFromInternet(String url){
    Future<new Foo> future = Foo.getFromInternet(url);
    return future.then((foo) -> {
        SubFoo subfoo = CONSTRUCT_FROM(foo);
        /* do whatever else you need to do to subFoo */
        return subFoo;
    });
}

What do you think? Does this add desirable qualities to OOP programming? Does it make it more robust? Less robust?

  • 2
    So... how are these different from constructors again? – Telastyn Apr 15 '16 at 20:39
  • @Telastyn There is no way to make construct public (or customize it - it initializes a zero object), and the generators can return anything (such as a subclass of Whatevs or a cached, immutable copy of Whatevs). – TheHansinator Apr 15 '16 at 20:45
  • 2
    So basically you are forcing people to use class factories. So I'd say that the disadvantage is "forces people to use class factories". – Steven Burnap Apr 15 '16 at 20:50
7

A basic problem I see with your approach, at least as you presented it here, is that it promotes mutability and side-effects. In fact, when trying to implement your suggestions closely, I couldn't even figure out how to create an immutable object at all! It looks like you promote constructing incomplete half-initialized objects and then initializing them in the factory.

That might just be an artifact of how you presented your idea, though. Also, if this is is what you intend, it would probably be possible to still have the language enforce that only fully constructed objects can leave a factory, and that e.g. setters or fields are only mutable inside a factory but are invisible and/or immutable after.

Here are some examples of existing languages that already have something related to what you propose:

and its successors (e.g. Self, Newspeak), as well as languages that follow in its tradition (e.g. ) or languages based on it (e.g. ) don't even have constructors. The only way to create an object is to call a factory method.

In Ruby and Smalltalk, this factory method is by convention called new. In Ruby, new actually just calls an even simpler factory method called allocate, which simply creates an empty object and returns it, then new calls a method named initialize on that newly allocated empty object. Basically, the whole thing looks like this:

class Class
  def new(*args, &block)
    new_obj = allocate
    new_obj.initialize(*args, &block)
    return new_obj
  end
end

[Note: in reality, initialize is usually private and thus must be called via reflection. Idiomatically, the entire thing would be more written like this, but I wanted to keep it simple to understand to non-Rubyists:

class Class
  def new(*args, &block)
    allocate.tap {|new_obj| new_obj.__send__(:initialize, *args, &block)}
  end
end

]

In general, you would create a new object using new:

foo = Foo.new

But you could just as well use:

foo = Foo.allocate.initialize # again, in reality, you'd need to use reflection

Another thing that you can see from this snippet: Ruby doesn't have static methods. Classes are just objects like any other object. They are instances of the class Class, and what looks like static methods are actually just regular instance methods of the Class class.

[Note: this leads to some funny circularities in Ruby's object model, mainly that Class is an object, and Object is a class, and since classes are objects which are instances of the Class class, and Class is a class, it is an instance of itself, as well as Object being a class, and thus an object, and thus an instance of Object, i.e. itself; and Class is a subclass of Object, which is itself an instance of Class …]

In Ruby, initialize is private by default, but new is public. You can, however, easily make it private if you wish:

class << Foo
  private :new
end

Objective-C is similar, except the methods are called alloc and init, and the idiomatic way is the opposite from Ruby. In Ruby, new is idiomatic and allocate/initialize is uncommon, in Objective-C it's the other way around:

Foo *foo = [[Foo alloc] init]; // idiomatic

Foo *foo = [Foo new];          // uncommon

has Factory Constructors, which are in some way related to what you are proposing. They don't make it impossible to call constructors, like you propose, but they allow something that you haven't considered: have a constructor that doesn't actually return a new instance of the class, in other words, have a factory that looks like a constructor.

From the ECMA-408 Dart Programming Language Specification, 4th Ed.:

Factories address classic weaknesses associated with constructors in other languages. Factories can produce instances that are not freshly allocated: they can come from a cache. Likewise, factories can return instances of different classes.

class Foo {
  int _param; // Dart uses underscore for privacy
  void doSomething() { /* … */ }
  void doSomethingWith(Foo other) { /* … */ }

  // a factory constructor
  factory Foo(int param) => new Foo._construct(param);

  // a named constructor, again underscore indicates privacy
  Foo._construct(this._param);
}

void main() {
  // note: factory looks indistinguishable from constructor
  final Foo fooOne = new Foo(1);

  final Foo fooTwo = new Foo._construct(2);
  // Unhandled exception:
  // No constructor 'Foo._construct' declared in class 'Foo'.
}

In Dart, unnamed constructors are public. You can make a private constructor, though, by using a named constructor whose name starts with _.

Let me address some of your statements:

I'll go ahead and add that one of the benefits that I can see is that constructors of objects can now follow all of the rules of other methods, following good naming patterns and programming conventions that overcome the limitations of constructors,

In Smalltalk, Self, Newspeak, Objective-C, and Ruby, this is trivially true, because there are no constructors, just normal methods. new is a method just like any other. You can call it anything you want, new is just a convention. For example, it is idiomatic for collection-like classes to have a factory method named [], which allows you to call e.g. Set[1, 2, 3] instead of (or as an alternative to) Set.new(1, 2, 3).

Dart has Named Constructors, which at least allow for one of your points: good naming patterns.

yet, through CONSTRUCT_FROM(base), allow inheritance.

Again, Ruby only has methods, and methods can be inherited.

In Dart, constructors are never inherited, by design, but you can e.g. have Redirecting Constructors that redirect to a different constructor or a parent constructor, and you can also explicitly invoke a parent constructor within a constructor.

For instance, I could create an object by interning:

This is actually the example used in the Dart documentation for factory constructors:

factory Foo(int param) {
  if (!_instances.contains(param)) {
    final Foo foo = new Foo._construct(param);
    _instances[param] = foo);
  }
  return _instances[param];
}

In Ruby, this is also trivial:

def Foo.make(param)
  instances[param] ||= new(param)
end

from extant this parameters,

Dart:

factory Foo.copy() {
  final Foo copy = new Foo._empty();
  /* copy parameters */
  return copy;
}

Ruby:

def Foo.copy
  copy = allocate
  // copy parameters
  copy
end

using subclasses,

Dart:

factory Foo.makeWithPrivileges() => new FooWithPrivileges.make();

Ruby:

def Foo.make_with_privileges
  FooWithPrivileges.new
end

Here's a running example implementing some of your snippets.

Ruby:

class Foo
  # everything below this line is private
  private

  # this generates a getter/setter method pair, also private
  attr_accessor :param

  # this method is called by new to initialize the object
  def initialize(param)
    self.param = param
  end

  # There is no equivalent to your static `make` method: `new` by 
  # default already does almost exactly what `make` does in your 
  # code.

  # only when we add the interning example, do we need to 
  # override `new`
  def self.new(param)
    @__instances__ ||= {}
    @__instances__[param] ||= super
  end

  public

  def copy
    copy = self.class.allocate
    instance_variables.each do |ivar|
      copy.instance_variable_set(ivar, instance_variable_get(ivar))
    end
    copy
  end

  def self.new_sub_foo(param)
    SubFoo.new(param)
  end
end

class SubFoo < Foo
end

foo_one = Foo.new(1)
# => #<Foo:0x007fa1341c06b0 @param=1>

foo_two = Foo.new(2)
# => #<Foo:0x007fa1340b92d0 @param=2>

foo_three = Foo.new(1)
# => #<Foo:0x007fa1341c06b0 @param=1>
# same object_id as foo_one because of interning

foo_four = foo_three.copy
# => #<Foo:0x007fa1342428b8 @param=1>
# same content but different object_id

sub_foo = Foo.new_sub_foo(1)
# => #<SubFoo:0x007ffafb171f30 @param=1>

Dart:

import 'dart:async';
import 'dart:io';
import 'dart:convert';

class Foo {
  // the underscore makes the field private
  int _param;

  // a named constructor
  // as above, the underscore makes it private
  // there is no body, Dart has initializer lists that do exactly 
  // what the trivial constructor body would do, i.e. assign the 
  // argument to the field
  Foo._internal(this._param);

  // an unnamed factory constructor
  // this one is the public interface and implements the interning
  static final Map<int, Foo> _instances = <int, Foo>{};

  factory Foo(int param) {
    if (!_instances.containsKey(param)) _instances[param] = new Foo._internal(param);
    return _instances[param];
  }

  Foo copy() {
    final Foo copy = new Foo._empty();
    copy._param = _param;
    return copy;
  }
  Foo._empty() : super();

  factory Foo.subFoo(int param) => new SubFoo._internal(param);

  @override toString() => "#$hashCode is $runtimeType with _param = $_param";

  static Future<Foo> getFromInternet(String uri) async {
    return new Foo(int.parse(await _getBody(uri)));
  }

  static Future<SubFoo> getSubFooFromInternet(String uri) async {
    return new SubFoo._internal(int.parse(await _getBody(uri)));
  }

  static Future<String> _getBody(String uri) async {
    HttpClient client = new HttpClient();
    HttpClientRequest request = await client.getUrl(Uri.parse(uri));
    HttpClientResponse response = await request.close();
    Stream<String> str = response.transform(UTF8.decoder);
    Future<String> res = str.first;
    return res;
  }
}

class SubFoo extends Foo {
  // remember, constructors aren’t inherited, we have to re-implement them
  SubFoo._internal(int param) : super._internal(param);
  SubFoo._empty() : super._empty();
}

void main() async {
  final Foo fooOne = new Foo(1);
  print("fooOne = $fooOne");
  // fooOne = #680267762 is Foo with _param = 1

  final Foo fooTwo = new Foo(2);
  print("fooTwo = $fooTwo");
  // fooTwo = #1071952692 is Foo with _param = 2

  final Foo fooThree = new Foo(1);
  print("fooThree = $fooThree");
  // fooThree = #680267762 is Foo with _param = 1
  // same hash code as fooOne because of interning

  final Foo fooFour = fooThree.copy();
  print("fooFour = $fooFour");
  // fooFour = #587955649 is Foo with _param = 1
  // same content but different hash code

  final SubFoo subFoo = new Foo.subFoo(1);
  print("subFoo = $subFoo");
  // subFoo = #697796960 is SubFoo with _param = 1

  final Future<Foo> asyncFoo = Foo.getFromInternet('https://random.org/integers/?num=1&min=1&max=100&col=1&base=10&format=plain');
  print("asyncFoo = ${await asyncFoo}");
  // asyncFoo = #865682649 is Foo with _param = 82

  final Future<SubFoo> asyncSubFoo = Foo.getSubFooFromInternet('https://random.org/integers/?num=1&min=1&max=100&col=1&base=10&format=plain');
  print("asyncSubFoo = ${await asyncSubFoo}");
  // asyncSubFoo = #512950715 is SubFoo with _param = 14
}
  • Thank you very much! This doesn't use the v-tables and memory overlay subclassing that helps languages like Java and C++ be fast (which is what I hoped to accomplish via CONSTRUCT_FROM()), but is otherwise exactly what I was looking for. I definitely will have to check out Ruby and Dart and see what it's like working in those languages. – TheHansinator Apr 16 '16 at 2:18
  • Well, in your subclass constructor examples, you construct two objects, one of which overlays the other. In my Ruby and Dart examples, only one object is ever constructed. Remember: in Ruby, it's the allocate method that allocates the object, and then the various initialize methods simply fill it with values, they don't construct anything new. The idea to have two objects that overlay each other doesn't make sense, because there is only one object to begin with. (If I understand you correctly.) – Jörg W Mittag Apr 16 '16 at 14:04
  • The object returned from CONSTRUCT_FROM is allocated in the same place as the base object - its fields are tacked onto the end. My CONSTRUCT is your allocate, and CONSTRUCT_FROM is a realloc followed by an init. – TheHansinator Apr 16 '16 at 14:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.