Privileged constructor members

  • Thread starter Michael Haufe (\TNO\)
  • Start date
M

Michael Haufe (\TNO\)

I'm attempting to use a design pattern for creating some constructor
functions, but I'm running into a bugbear of verbosity that I hope
someone could enlighten me with in re-factoring towards a more
straightforward solution.

Here's an example of one of the constructors using the design:

-------------------------------------

function XmlDeclaration(version, encoding, standalone) {
var _encoding,
_len = arguments.length,
_standalone,
_version;

if(_len !== 3 && _len !== 1) {
throw new Error("Invalid arguments");
} else if(version instanceof XmlDeclaration ){
_encoding = version.encoding;
_standalone = version.standalone;
_version = version.version;
} else if(typeof version === "string" &&
typeof encoding === "string" &&
typeof standalone === "string") {

_encoding = encoding;
_version = version;
_standalone = standalone;
} else {
throw new TypeError("Invalid arguments");
}

this.__defineGetter__("encoding",function() {
return _encoding
})
this.__defineSetter__("encoding",function(v) {
if(typeof v === "string") {
_encoding = v
} else {
throw new TypeError("Invalid property assignment. A string is
expected.")
}
})
this.__defineGetter__("standalone",function()
return _standalone
})
this.__defineSetter__("standalone",function(v){
if(typeof v === "string"){
_standalone = v
}else{
throw new TypeError("Invalid property assignment. A string is
expected.")
}
})
this.__defineGetter__("version",function(){
return _version
})
this.__defineSetter__("version",function(v){
if(typeof v === "string"){
_version = v
}else{
throw new TypeError("Invalid property assignment. A string is
expected.")
}
})
this.toString = function(){
return "<" + "?xml version=\"" + _version + "\" encoding=\"" +
_encoding + "\" standalone=\"" + _standalone + "\"?" + " >"
}
}

var foo = new XmlDeclaration("1.0", "utf-8", "yes")
foo.encoding = "utf-16"
foo.toString() //<?xml version="1.0" encoding="utf-16"
standalone="yes" ?>
 
T

Thomas 'PointedEars' Lahn

Michael said:
I'm attempting to use a design pattern for creating some constructor
functions, but I'm running into a bugbear of verbosity that I hope
someone could enlighten me with in re-factoring towards a more
straightforward solution.

Here's an example of one of the constructors using the design:
[...]

var foo = new XmlDeclaration("1.0", "utf-8", "yes")
foo.encoding = "utf-16"
foo.toString() //<?xml version="1.0" encoding="utf-16"
standalone="yes" ?>

Unfortunately, while your solution looks interesting,
you have failed to ask a smart question (about it).

<http://www.catb.org/~esr/faqs/smart-questions.html>


PointedEars
 
T

-TNO-

Unfortunately, while your solution looks interesting,
you have failed to ask a smart question (about it).

I'll try to clarify my meaning then: Is there a less verbose approach
to my method for implementing privileged accessor properties & methods
for a constructor function?
 
T

Thomas 'PointedEars' Lahn

-TNO- said:
I'll try to clarify my meaning then: Is there a less verbose approach
to my method for implementing privileged accessor properties & methods
for a constructor function?

Please try again and define "less verbose".

You are _not_ "implementing privileged accessor properties and methods for a
constructor function", but you attempt to implement them for objects created
with that constructor.

What exactly do you want to achieve, and where is it supposed to work? As
for your code, because of Object.prototype.__defineSetter__() it works only
in JavaScriptâ„¢ 1.5+ to begin with (AFAIK).


PointedEars
 
J

John G Harris

I'm attempting to use a design pattern for creating some constructor
functions, but I'm running into a bugbear of verbosity that I hope
someone could enlighten me with in re-factoring towards a more
straightforward solution.

Here's an example of one of the constructors using the design:

-------------------------------------

function XmlDeclaration(version, encoding, standalone) {
var _encoding,
_len = arguments.length,
_standalone,
_version;

if(_len !== 3 && _len !== 1) {
throw new Error("Invalid arguments");
} else if(version instanceof XmlDeclaration ){
_encoding = version.encoding;
_standalone = version.standalone;
_version = version.version;
} else if(typeof version === "string" &&
typeof encoding === "string" &&
typeof standalone === "string") {

_encoding = encoding;
_version = version;
_standalone = standalone;
} else {
throw new TypeError("Invalid arguments");
}
<snip>

You could decree that the programmers using this function are grown up
and can supply sensible parameter values without you vetting them. The
result would be code that is faster, is easier to proof-read, is easier
to maintain, and requires less testing which reduces development cost
and helps the project to finish sooner.

John
 
R

RobG

<snip>

You could decree that the programmers using this function are grown up
and can supply sensible parameter values without you vetting them. The
result would be code that is faster, is easier to proof-read, is easier
to maintain, and requires less testing which reduces development cost
and helps the project to finish sooner.

Good point. What is required is that the values passed resolve to
appropriate values when evaluated. Provided the value passed to
version resolves to a suitable string when evaluted, then it is a
suiable value - it doesn't necessarily need to be a string primitive.

Also, simply checking that version is a string is not a thorough
test, it seems it should be a numeric string of format n.n, so if a
validation test is to be employed, a regular expression test like:

...
} else if(version instanceof XmlDeclaration ){
...
} else {

version = String(version);

if ((/\d\.\d/).test(version)) {
// version is OK
}

// similar tests for other parameters

}

might be better.
 
T

-TNO-

You're problem is interesting.  You seem to have done copy-and-paste
coding, and are not happy with it.

I'm not sure how you came to that conclusion, but yes I wasn't happy
with it. You're code example sparked a new idea though for an
approach I think is a significant improvement in readability:

------------------------------------------------------------
function XmlDeclaration(version, encoding, standalone) {
var _encoding,
_len = arguments.length,
_standalone,
_version;

if (_len !== 3 && _len !== 1) {
throw new Error("Invalid arguments");
} else if(version instanceof XmlDeclaration ) {
_encoding = version.encoding;
_standalone = version.standalone;
_version = version.version;
} else if(typeof version === "string" &&
typeof encoding === "string" &&
typeof standalone === "string") {
_encoding = encoding;
_version = version;
_standalone = standalone;
} else {
throw new TypeError("Invalid arguments");
}

Object.extend(this,{
get encoding(){
return _encoding
},
set encoding(v){
if(typeof v === "string")
_encoding = v
else
throw new TypeError("Invalid property assignment.
A string is expected.")
},
get standalone(){
return _standalone
},
set standalone(v){
if(typeof v === "string")
_standalone = v
else
throw new TypeError("Invalid property assignment.
A string is expected.")
},
get version(){
return _version
},
set version(v){
if(typeof v === "string")
_version = v
else
throw new TypeError("Invalid property assignment.
A string is expected.")
}
})

this.toString = function(){
return "<" + "?xml version=\"" + _version + "\" encoding=
\"" +
_encoding + "\" standalone=\"" + _standalone +
"\"?" + " >"
}
}

------------------------------------------------------------
 
T

-TNO-

Please try again and define "less verbose".

"less verbose" is defined in this context the same way as it is in
common usage: "fewer words"
You are _not_ "implementing privileged accessor properties and methods for a
constructor function", but you attempt to implement them for objects created
with that constructor.

Yes, that is the goal I did not communicate properly
What exactly do you want to achieve,

What I just achieved in my last message
and where is it supposed to work?

I believe this is an irrelevant question
 As for your code, because of Object.prototype.__defineSetter__() it works only
in JavaScript™ 1.5+ to begin with (AFAIK)

Yes, I am aware of implementation support, and this is not an issue in
the environment I am using this for.
 
T

-TNO-

You could decree that the programmers using this function are grown up
and can supply sensible parameter values without you vetting them.

Any sufficiently large code base needs a contract system, especially
in a team development environment where not all members are
equivalently skilled.
The result would be code that is faster...

A relatively small gain in execution speed is not worth the loss of a
"guarantee" on the value types being passed as parameters.
is easier to proof-read, is easier to maintain, and requires less testing....
On would think that removing type checks would have the opposite
effect.
which reduces development cost
But does not reduce maintenance cost when debugging
and helps the project to finish sooner.
Not all developers can code and forget, some of us actually maintain a
code base for years and have to see beyond the immediate goal to the
net costs of a project. This is off-topic though, and not worth
pursuing.
 
T

-TNO-

Also,  simply checking that version is a string is not a thorough
test, it seems it should be a numeric string of format n.n, so if a
validation test is to be employed, a regular expression test like:

  ...
  } else if(version instanceof XmlDeclaration ){
    ...
  } else {

    version = String(version);

    if ((/\d\.\d/).test(version)) {
      // version is OK
    }

    // similar tests for other parameters

  }

might be better.

An excellent point and its well taken, but this was not really the
focus of my question . My concern was primarily aimed at the getter/
setter pairs.
 
R

RobG

An excellent point and its well taken, but this was not really the
focus of my question . My concern was primarily aimed at the getter/
setter pairs.

Ok, so a classic prototype inheritance scheme for getters and setters
(not using features of JavaScript 1.5) would use something like:

function XmlDeclaration(version, encoding, standalone) {
var _encoding,
_len = arguments.length,
_standalone,
_version;

// Do parameter checking and value assignment

}

XmlDeclaration.prototype = {

version: function() {
if (arguments.length > 0) {
// set value using arguments
} else {
return this._version;
}
},

encoding: function() {
if (arguments.length > 0) {
// set value using arguments
} else {
return this._encoding;
}
},

standalone: function() {
if (arguments.length > 0) {
// set value using arguments
} else {
return this._standalone;
}
}
}

However, that leaves the values available as public properties. To use
private members and privileged methods, follow the pattern described
by Douglas Crockford:

"Private Members in JavaScript"
<URL: http://www.crockford.com/javascript/private.html >
 
T

-TNO-

However, that leaves the values available as public properties. To use
private members and privileged methods, follow the pattern described
by Douglas Crockford:

"Private Members in JavaScript"
<URL:http://www.crockford.com/javascript/private.html>

Here is a simplified re-posting of the pattern I've come up with that
avoids this problem:

-------------------------------------------------------------------

function XmlDeclaration(version, encoding, standalone) {
var _encoding,
_len = arguments.length,
_standalone,
_version;

//...

Object.extend(this,{
get encoding(){
//...
},
set encoding(v){
//...
},
get standalone(){
//...
},
set standalone(v){
//...
},
get version(){
//...
},
set version(v){
//...
}
})

this.toString = function(){
//...
}

}
 
T

Thomas 'PointedEars' Lahn

RobG said:
That should be:

_version = String(version);

if ((/\d\.\d/).test(_version)) {
// _version is OK
}

Explicit type conversion is not necessary here.


PointedEars
 
G

Garrett Smith

Michael said:
I'm attempting to use a design pattern for creating some constructor
functions, but I'm running into a bugbear of verbosity that I hope
someone could enlighten me with in re-factoring towards a more
straightforward solution.

I don't know just what your subject "privileged constructor members" means.

Hidden members are possible using closures. A hidden member can be a
constructor function. Instances of that hidden constructor can be
created and stored in an object, keyed by the id of the "public" object.
(this requires the object to *have* an id).
Here's an example of one of the constructors using the design:

-------------------------------------

[please use spaces, not tabs]

Dynamic overloading makes things more complicated. Avoid that.

Using - arguments - object prevents a fairly well-known optimization.
Avoid that, too.

Passing an object is pretty straightforward:-

var xmlDeclaration = new XmlDeclaration(
{ version: 1, encoding: "utf-8", standalone : true };
);

Though it might good enough to forgo the constructor altogether:-


var xmlDeclarationProps = {
version: 1,
encoding: "utf-8",
standalone : true
};

.. That seems straightforward.

If the program is more concerned with printing, then this strategy might
make sense.

printXML( xmlDeclarationProps );

Hard to say from here.
var foo = new XmlDeclaration("1.0", "utf-8", "yes")
foo.encoding = "utf-16"
foo.toString() //<?xml version="1.0" encoding="utf-16"
standalone="yes" ?>

What type system?

A closure can be used to hide memembers.

(function(){
self.XmlDeclaration = XmlDeclaration;
var components = {};

function XmlDeclaration(id, enc, standalone){
this.id = id;
components[id] = new Component(enc, standalone);
}

XmlDeclaration.prototype = {
setEncoding : function(enc){
components[this.id].encoding = enc;
},

toXmlString : function() {
var c = components[this.id];
return ('<?xml version="1.0" encoding="' + c.encoding +'"'
+ '> standalone="' + c.standalone +'" ?>');
}
};

function Component(encoding, standalone){
this.encoding = encoding
this.standalone = standalone;
}
})();

var foo = new XmlDeclaration("1.0", "utf-8", "yes");
foo.setEncoding("utf-16");
foo.toXmlString();
=> "<?xml version="1.0" encoding="utf-16"> standalone="yes" ?>"

In this case, creating another object whose one-and-only purpose is to
hide what would otherwise have been "encoding" and "standalone".

Private members are useful when the object to be hidden is serving an
actual role. In this case, an abstraction is introduced for no good reason.

That is how to make what I would call a "private static class". I don't
like it here, and I don't know just what your subject "privileged
constructor members" means.

Garrett
 
T

-TNO-

I don't know just what your subject "privileged constructor members" means.
Hidden members are possible using closures. A hidden member can be a
constructor function.

The goal was to obtain public getter/setter properties for instances
of a constructor that have access to the private variables of the
instance. My first code example accomplishes that, but in a verbose
way I was hoping to simplify to obtain the same effect.
[please use spaces, not tabs]

Indeed, I didn't realize the width limit until after I had posted.
Using - arguments - object prevents a fairly well-known optimization.

So I've heard. Counting arguments is probably excessive and
unnecessary in 99% of cases anyway.
What type system?

A type & value type system is on the drawing board along with object
initializer extensions, though the final forms are still in debate:

http://wiki.ecmascript.org/doku.php?id=strawman:types
http://wiki.ecmascript.org/doku.php?id=strawman:object_initialiser_extensions
Private members are useful when the object to be hidden is serving an
actual role. In this case, an abstraction is introduced for no good reason.

This case was used primarily as an example of the the pattern.
I don't know just what your subject "privileged
constructor members" means.

No doubt a misnomer, but I couldn't think of a better subject at the
time to convey the message "defining getter and setter properties in a
constructor with access to private variables" in so few words.
 
T

Thomas 'PointedEars' Lahn

-TNO- said:
[please use spaces, not tabs]

Indeed, I didn't realize the width limit until after I had posted.

Although related, IMHO the reason for recommending a number of spaces (<SP>)
above horizontal tab characters (<HT>) in NetNews messages (and for some, in
source code in general) is not so much that tabs wastes more space but that
tab characters they are not uniformly displayed. Some applications display
tabbed text so that it is aligned with a grid, others so that each <HT>
character takes up the space that would be taken by a fixed number of

Not using `arguments' where it should be used, or going to great lengths to
avoid using `arguments' when not necessary, makes code more error-prone and
harder to maintain. A good example of that is the shortsighted
recommendation to name one's function expressions in order to avoid using
`arguments.callee' ("because it is inefficient", "because ES 3.1/5 Strict
Mode does not allow it" etc.); that identifier will leak into the outer
scope in all known implementations of JScript that support named function
expressions (google "function statement").
So I've heard. Counting arguments is probably excessive and
unnecessary in 99% of cases anyway.

Probably not. Not as long as there is no type system that differentiates
between an argument not passed and an argument that has the `undefined' value.
A type & value type system is on the drawing board along with object
initializer extensions, though the final forms are still in debate:

http://wiki.ecmascript.org/doku.php?id=strawman:types
http://wiki.ecmascript.org/doku.php?id=strawman:object_initialiser_extensions

Although the opposite has happened before, one should not assume that a
working draft is going to be implemented.
No doubt a misnomer, but I couldn't think of a better subject at the
time to convey the message "defining getter and setter properties in a
constructor with access to private variables" in so few words.

"Protected properties and methods" would probably have been understood, as
it is used in Crockford's approach. (AFAIK, he was the first to suggest the
pattern.)


PointedEars
 
G

Garrett Smith

Thomas said:
-TNO- said:
[please use spaces, not tabs]
Indeed, I didn't realize the width limit until after I had posted.

Although related, IMHO the reason for recommending a number of spaces (<SP>)
above horizontal tab characters (<HT>) in NetNews messages (and for some, in
source code in general) is not so much that tabs wastes more space but that
tab characters they are not uniformly displayed. Some applications display
tabbed text so that it is aligned with a grid, others so that each <HT>
character takes up the space that would be taken by a fixed number of

Not using `arguments' where it should be used, or going to great lengths to
avoid using `arguments' when not necessary,

Using - arguments - is not necessary here. TNO asked for help in
"refactoring towards a more straightforward solution."

makes code more error-prone and
harder to maintain. A good example of that is the shortsighted
recommendation to name one's function expressions in order to avoid using
`arguments.callee' ("because it is inefficient", "because ES 3.1/5 Strict
Mode does not allow it" etc.); that identifier will leak into the outer
scope in all known implementations of JScript that support named function
expressions (google "function statement").

Using optional middle arguments can tend to overcomplicate the method
were it is used.
Probably not. Not as long as there is no type system that differentiates
between an argument not passed and an argument that has the `undefined' value.

Checking the presence of a property needs an object. Introducing a value
object does that. Properties of that object are keyed, not ordered and
checking to see which is omitted is easy.

One could argue that checking the arguments object is easy, too, but the
arguments object's keys are numbers, and that doesn't tell you very well
what the caller was intending to pass, only what was passed. Using the
"check the arguments object" strategy, the program can further try to
determine what was intended to be passed by type checking the parameter
variables to see if - param3 - is missing and type of - param2 - matches
what type of - param3 - is expected to be:-

function complexProcedure(stringP1, objP2, funP3) {
if(arguments.length == 2) {
// caller may have omitted objP2.
if(typeof objP2 == "function") {
// what is the caller attempting?
}
}
}

Typechecking parameters for faux-overloading is messy. It is also error
prone because the typeof operator is unpredictable with host object.
Although the opposite has happened before, one should not assume that a
working draft is going to be implemented.

Types were thrown out before. I don't know if it will be in again or not.

I don't have available time right now to read the proposal. Eclipse is
back up again and so I am back to work.

Regards,

Garrett
 
T

Thomas 'PointedEars' Lahn

Garrett said:
Thomas said:
-TNO- said:
[please use spaces, not tabs]
Indeed, I didn't realize the width limit until after I had posted.
Although related, IMHO the reason for recommending a number of spaces (<SP>)
above horizontal tab characters (<HT>) in NetNews messages (and for some, in
source code in general) is not so much that tabs wastes more space but that
tab characters they are not uniformly displayed. Some applications display
tabbed text so that it is aligned with a grid, others so that each <HT>
character takes up the space that would be taken by a fixed number of
it said:
Using - arguments - object prevents a fairly well-known optimization.
Not using `arguments' where it should be used, or going to great lengths to
avoid using `arguments' when not necessary,

Using - arguments - is not necessary here. TNO asked for help in
"refactoring towards a more straightforward solution."

You seem to have missed that the thread has drifted.
Using optional middle arguments can tend to overcomplicate the method
were it is used.

Non sequitur. This applies to all optional arguments.
Checking the presence of a property needs an object.

That does not have to do anything with it.
Introducing a value object does that.

It is not always practical to have an object created just to pass arguments,
and it can waste a lot of precious memory.
Properties of that object are keyed, not ordered and
checking to see which is omitted is easy.

It is not. Built-in inherited properties may not be used for parameter
names of the "value object".
One could argue that checking the arguments object is easy, too, but the
arguments object's keys are numbers,

There are no keys, there are property names. Learn to use proper terminology.
and that doesn't tell you very well what the caller was intending to pass,
only what was passed.

So do your "value objects".
Using the
"check the arguments object" strategy, the program can further try to
determine what was intended to be passed by type checking the parameter
variables to see if - param3 - is missing and type of - param2 - matches
what type of - param3 - is expected to be:-

function complexProcedure(stringP1, objP2, funP3) {
if(arguments.length == 2) {
// caller may have omitted objP2.
if(typeof objP2 == "function") {
// what is the caller attempting?
}
}
}

Typechecking parameters for faux-overloading is messy. It is also error
prone because the typeof operator is unpredictable with host object.

While partially true, none of this has to do with the matter discussed.
Types were thrown out before. I don't know if it will be in again or not.

And it would not matter anyway as you missed the point of my statement.
I don't have available time right now to read the proposal. Eclipse is
back up again and so I am back to work.

You wrote all of this while Eclipse was starting? Either you are an
incredibly quick typist, or you are using the wrong OS, or the wrong
eclipse.ini settings (BTDT).


PointedEars
 
T

Thomas 'PointedEars' Lahn

Thomas said:
Garrett said:
Thomas said:
-TNO- wrote:
[...] Counting arguments is probably excessive and
unnecessary in 99% of cases anyway. [...]
Probably not. Not as long as there is no type system that differentiates
between an argument not passed and an argument that has the `undefined' value.
Checking the presence of a property needs an object. [...]
Introducing a value object does that. [...]
Properties of that object are keyed, not ordered and
checking to see which is omitted is easy.

It is not. Built-in inherited properties may not be used for parameter
names of the "value object".

Sorry for potential gibberish. I meant to say:

Names of built-in properties (inherited or not) may not be used as names
of user-defined properties of the "value object" that are used as an
alternative way for passing argument values.

Supplemental:

An implementation that maps those property names to "harmless" (unused)
names accordingly (like that I posted recently) can avoid that problem. But
it adds both complexity and decreases the efficiency of the call, and thus
cannot be recommended for that purpose.


PointedEars
 

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,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top