Controlling acces to object instantiation?

A

Andrew S. Townley

Hello everyone,

Trying to get my head around some of the subtleties of Ruby. I've done
quite a bit of C/C++/Java and some Python programming, and the normal
way that I'd solve this problem (in pseudo-Java) would be:

interface SomeFeature
{
void execute();
}

public class FeatureFactory
{
private static class FeatureImpl implements SomeFeature
{
public FeatureImpl(String initParam1,
String initParam2)
{
...
}

public void execute() { ... }
}

public SomeFeature createFeature()
{
return new FeatureImpl("param1", "param2");
}
}

I've seen some of the previous threads on the list (starting from here:
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/60616), but
I don't see anything immediately obvious as to how to encapsulate the
creation of my concrete implementations of a common interface (even with
duck typing, there's an implied interface).

The main issue here is that I want to prevent the client code from
instantiating their own objects. The objects should be created only by
a factory of some sort (who has implementation-specific configuration
information about how to bootstrap them).

It would appear that if you have 'require'd the appropriate files (or
can), you can create any instances you want. Is there a way to
accomplish what I'm trying to do beyond saying "don't do that" in the
documentation?

Any assistance would be appreciated.

Thanks in advance,

ast

***************************************************************************************************
The information in this email is confidential and may be legally privileged. Access to this email by anyone other than the intended addressee is unauthorized. If you are not the intended recipient of this message, any review, disclosure, copying, distribution, retention, or any action taken or omitted to be taken in reliance on it is prohibited and may be unlawful. If you are not the intended recipient, please reply to or forward a copy of this message to the sender and delete the message, any attachments, and any copies thereof from your system.
***************************************************************************************************
 
G

Gavin Kistner

--Apple-Mail-1-128133013
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=US-ASCII;
delsp=yes;
format=flowed

The main issue here is that I want to prevent the client code from
instantiating their own objects. The objects should be created
only by
a factory of some sort (who has implementation-specific configuration
information about how to bootstrap them).


class Foo
def self.make
self.new( rand )
end

def initialize( id )
@id = id
end

class << self
protected :new
end
end

p f1 = Foo.make
# => #<Foo:0x340858 @id=0.452814124524593>

p f2 = Foo.new( 12 )
# => /Users/gkistner/Desktop/tmp.rb:16: protected method `new' called
for Foo:Class (NoMethodError)


--Apple-Mail-1-128133013--
 
A

Andrew S. Townley

Cheers, Gavin. Thanks for the quick reply. I figured there had to be a
way, but it wasn't something I remember reading in the pickaxe book or
anywhere else that I'd seen so far.

Thanks again,

ast

class Foo
def self.make
self.new( rand )
end

def initialize( id )
@id = id
end

class << self
protected :new
end
end

p f1 = Foo.make
# => #<Foo:0x340858 @id=0.452814124524593>

p f2 = Foo.new( 12 )
# => /Users/gkistner/Desktop/tmp.rb:16: protected method `new' called
for Foo:Class (NoMethodError)

***************************************************************************************************
The information in this email is confidential and may be legally privileged. Access to this email by anyone other than the intended addressee is unauthorized. If you are not the intended recipient of this message, any review, disclosure, copying, distribution, retention, or any action taken or omitted to be taken in reliance on it is prohibited and may be unlawful. If you are not the intended recipient, please reply to or forward a copy of this message to the sender and delete the message, any attachments, and any copies thereof from your system.
***************************************************************************************************
 
A

Ara.T.Howard

Hello everyone,

Trying to get my head around some of the subtleties of Ruby. I've done
quite a bit of C/C++/Java and some Python programming, and the normal
way that I'd solve this problem (in pseudo-Java) would be:

interface SomeFeature
{
void execute();
}

public class FeatureFactory
{
private static class FeatureImpl implements SomeFeature
{
public FeatureImpl(String initParam1,
String initParam2)
{
...
}

public void execute() { ... }
}

public SomeFeature createFeature()
{
return new FeatureImpl("param1", "param2");
}
}

I've seen some of the previous threads on the list (starting from here:
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/60616), but
I don't see anything immediately obvious as to how to encapsulate the
creation of my concrete implementations of a common interface (even with
duck typing, there's an implied interface).

The main issue here is that I want to prevent the client code from
instantiating their own objects. The objects should be created only by
a factory of some sort (who has implementation-specific configuration
information about how to bootstrap them).

It would appear that if you have 'require'd the appropriate files (or
can), you can create any instances you want. Is there a way to
accomplish what I'm trying to do beyond saying "don't do that" in the
documentation?

Any assistance would be appreciated.

Thanks in advance,

harp:~ > cat a.rb
module Factory
@classes = {
'a' => (
Class::new do
class << self
def foo; 42; end
end
def bar; 42.0; end
end
),
'b' => (
Class::new do
class << self
def foo; 'FORTY-TWO'; end
end
def bar; 'forty-two'; end
end
),
}
def self::new(*a, &b)
class_for(a.shift)::new(*a, &b)
end
def self::class_for typename
@classes[typename]
end
end

a_obj = Factory::new 'a'
b_obj = Factory::new 'b'

a_class = Factory::class_for 'a'
b_class = Factory::class_for 'b'

p a_obj.bar
p b_obj.bar

p a_class::foo
p b_class::foo



harp:~ > ruby a.rb
42.0
"forty-two"
42
"FORTY-TWO"


obviously you would want 'class_for' to be private - but this illustrates the
general idea.

hth.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| Your life dwells amoung the causes of death
| Like a lamp standing in a strong breeze. --Nagarjuna
===============================================================================
 
A

Andrew S. Townley

Hi Ara,

Hmmm... interesting approach. I'm not sure I quite need the indirection
you've illustrated, but it's interesting to see all the different ways
you can solve the problem in Ruby. The passing the class declaration as
a block to the Class:new method will require me to do some more
reading. I haven't looked into the reflection/class operations much
yet. I want to make sure that I understand exactly what's happening
here. Instinctively, I think I understand what's going on, but need to
double-check. Some things are still Ruby-101 to me at this stage.

Also, I have a question further down...

harp:~ > cat a.rb
module Factory
@classes = {
'a' => (
Class::new do
class << self
def foo; 42; end
end
def bar; 42.0; end
end
),
'b' => (
Class::new do
class << self
def foo; 'FORTY-TWO'; end
end
def bar; 'forty-two'; end
end
),
}
def self::new(*a, &b)
class_for(a.shift)::new(*a, &b)

I'm assuming you're doing the 'a.shift' call here to allow you to
specify all of the arguments you might need to the constructor. Is that
correct? Thus you could potentially write something like:

ref = Factory::new 'a', 'param1', 'param2'

which would allow you to have the class 'a' support a constructor taking
the two parameters (and, of course, the optional block).

Anyway, thanks for the neat example! :)

ast

***************************************************************************************************
The information in this email is confidential and may be legally privileged. Access to this email by anyone other than the intended addressee is unauthorized. If you are not the intended recipient of this message, any review, disclosure, copying, distribution, retention, or any action taken or omitted to be taken in reliance on it is prohibited and may be unlawful. If you are not the intended recipient, please reply to or forward a copy of this message to the sender and delete the message, any attachments, and any copies thereof from your system.
***************************************************************************************************
 
A

Ara.T.Howard

Hmmm... interesting approach. I'm not sure I quite need the indirection
you've illustrated, but it's interesting to see all the different ways you
can solve the problem in Ruby. The passing the class declaration as a block
to the Class:new method will require me to do some more reading. I haven't
looked into the reflection/class operations much yet. I want to make sure
that I understand exactly what's happening here. Instinctively, I think I
understand what's going on, but need to double-check. Some things are still
Ruby-101 to me at this stage.

i agree it's overkill - but i does prevent the classes from being introduced
as valid constants when the file is required and it allows you you basically
have the classes themselves as private instance variables. this is about as
protected as you can get in ruby - since there's always instance_eval...
nevertheless it does afford total control over how these objects get created.
Also, I have a question further down...



I'm assuming you're doing the 'a.shift' call here to allow you to specify
all of the arguments you might need to the constructor. Is that correct?
Thus you could potentially write something like:

ref = Factory::new 'a', 'param1', 'param2'

which would allow you to have the class 'a' support a constructor taking the
two parameters (and, of course, the optional block).

exactly - in reality you'd probably determine which class to do based on the
args alone, or something else...

cheers.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| Your life dwells amoung the causes of death
| Like a lamp standing in a strong breeze. --Nagarjuna
===============================================================================
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,997
Messages
2,570,241
Members
46,831
Latest member
RusselWill

Latest Threads

Top