How to express DAO class model in JS?

N

nick

Hello again! Today I have something a little more concrete for you
guys to muse over. In fact I doubt it will require much heavy musing
for some of you, being the prolific js experts you are. ;) Here's the
situation:

I have a UML class diagram I've made to describe the data model of a
web app I'm developing. I've written some XSL transformations to
generate SQL and PHP code from the model. Essentially, each class in
the model represents a table in the database, and a class in the PHP
model.

Each attribute in the model represents a column in the database (or a
linking table, for attributes with MTM relationships) and a property
of a class. All attributes are created as private properties, accessed
via a public Get_ and Set_ method created for each. If an attribute is
a complex type, the class will not instantiate it until the Getter is
called. If it's not instantiated, it can be retrieved from the DB at
that time.

Now I bet you're wonding, "what the heck does all this have to do with
javascript?" Well, I decided as long as I'm generating classes, I
might as well generate matching JS classes for use on the client. The
JS classes can have the same API as the PHP classes; the method
signatures can even match and the JS classes can make AJAX calls to
their counterpart PHP classes to set and retrieve data.

So, on with my question: What's the best pattern to use to create
these pseudoclasses? I'm particularly unsure of what to do about the
private properties, as from a few simple tests I've run "privileged"
methods with access to "private" properties (variables declared with
var, not properties of 'this') do not work so well once inheritance is
introduced. I'm seriously leaning toward making all the 'private'
properties private in name only... they are still accessible via
this.whatever, and just hope the programmers who end up working on
this thing use the GetWhatever and SetWhatever syntax instead.

Maybe a code sample will explain what I'm doing better. Here is one of
the generated classes. I've stripped out a bunch of jsdoc comments and
DAO methods, hopefully enough is left to give an idea of what I'm up
to.
....

//... following block is in another file ...

Function.prototype.extend = function(cls)
{
this.prototype = new cls;
this.prototype.constructor = this;
this.prototype._super = cls;
}

//... more generated stuff goes here...

Photo_Base.extend(GalleryApp);
this.Photo_Base = (function(){

// constructor
function Photo_Base()
{
this.Id = null;
this.Link = null;
this.Caption = null;
}

Photo_Base.prototype.GetId = function ()
{
return this.Id ? this.Id : 'null';
}

Photo_Base.prototype.SetId = function (value)
{
this.Id = value;
}

Photo_Base.prototype.GetLink = function ()
{
return this.Link;
}

Photo_Base.prototype.SetLink = function (value)
{
this.Link = value;
return this;
}

Photo_Base.prototype.GetCaption = function ()
{
return this.Caption;
}

Photo_Base.prototype.SetCaption = function (value)
{
this.Caption = value;
return this;
}

return Photo_Base;

})(); // end of class Photo_Base.

....
So, how does this design look? Will it work, or do I need to take this
back to the drawing board? Is there some trick that will allow me to
use "protected" variables (say, 'var Caption' instead of
'this.Caption') that will not stay attached to their old class scope
when the class is extended? I hope this makes at least some sense.
Thanks again, guys,

-- Nick
 
A

Asen Bozhilov

nick said:
So, how does this design look? Will it work, or do I need to take this
back to the drawing board? Is there some trick that will allow me to
use "protected" variables (say, 'var Caption' instead of
'this.Caption') that will not stay attached to their old class scope
when the class is extended? I hope this makes at least some sense.
Thanks again, guys,

There are no "private", "static", "protected", "final", etc. In
ECMAScript each properties are public, except internal properties of
objects e.g.:

[[Get]], [[Put]], [[Scope]], etc.

I use naming convention for this things and proper documentation of my
code.

/**
* Private property
* @private
* @type Number
*/
o._privateProperty = 10;

/**
* Final property
* @final
* @type Number
*/
o.FINAL_PROPERTY = 10;

And someone if want to use my code, he can read documentation and code
will be works in expected way, without dummy patch for private, final
and etc.
 
N

nick

nick said:
So, how does this design look? Will it work, or do I need to take this
back to the drawing board? Is there some trick that will allow me to
use "protected" variables (say, 'var Caption' instead of
'this.Caption') that will not stay attached to their old class scope
when the class is extended? I hope this makes at least some sense.
Thanks again, guys,

There are no "private", "static", "protected", "final", etc. In
ECMAScript each properties are public, except internal properties of
objects e.g.:

[[Get]], [[Put]], [[Scope]], etc.

I use naming convention for this things and proper documentation of my
code.

/**
 * Private property
 * @private
 * @type Number
 */
o._privateProperty = 10;

/**
 * Final property
 * @final
 * @type Number
 */
o.FINAL_PROPERTY = 10;

And someone if want to use my code, he can read documentation and code
will be works in expected way, without dummy patch for private, final
and etc.

Makes sense to me... I'm pretty much going that route myself. What I'm
really wondering about is the pattern I'm using to create these class
definitions. I kind of screwed it up in my previous post, but here's
what it looks like:
....

Function.prototype.extend = function(cls)
{
this.prototype = new cls;
this.prototype.constructor = this;
this.prototype._super = cls;
}

// ...

this.TestA = (function(){
// constructor
function TestA (x, y)
{
this.a = x;
this.b = y;
}
TestA.prototype.talk = function()
{
alert("hi "+this.a+' '+this.b)
}
return TestA;
})();

// ...

this.TestB = (function(){
TestB.extend(TestA);
// constructor
function TestB (x, y)
{
this.a = y;
this.b = x;
}
return TestB;
})();


....
I haven't actually seen this pattern described anywhere, but it seems
to work well. Does it have a name? Will it work okay or is there
something else I should be doing?
 
S

Scott Sauyet

So, on with my question: What's the best pattern to use to create
these pseudoclasses? I'm particularly unsure of what to do about the
private properties, as from a few simple tests I've run "privileged"
methods with access to "private" properties (variables declared with
var, not properties of 'this') do not work so well once inheritance is
introduced.

Douglas Crockford details one method of creating private properties:

http://javascript.crockford.com/private.html

I don't know how well you could mimic Java's "protected" members,
though.

I'm wondering what you need all this *for* though. Do you really need
what are essentially Javascript proxy classes to your PHP ones? I'm
certainly not trying to say you don't, but even in fairly complex
projects, I've never found any need for this.

-- Scott
 
N

nick

Douglas Crockford details one method of creating private properties:

   http://javascript.crockford.com/private.html

Yeah, the thing i dislike about Crockford's privates (heh) is that the
constructors get really heavy... All the private properties end up
there, which isn't so bad, but then all the "protected" functions end
up there too, which means you need some ugly construction like "if
(proto.isinitted) return; proto.isinitted = 1;" before your method
definitions if you don't want them to be redefined every time you call
the constructor (which includes once in the call to "extends" in this
pattern).

But, that's all beside the point... the problem I have is that those
privates rally act *private*, not protected, so in a "protected"
method inherited by a derived class I still get the value from the
superclass' constructor.

Let's throw all that stuff in the constructor and see what happens.
....

this.TestA = (function(){
// constructor
function TestA ()
{
var myPvt = ' do not touch ';
TestA.prototype.talk = function(){alert(myPvt)}
}
return TestA;
})();

this.TestB = (function(){
TestB.extend(TestA);
// constructor
function TestB ()
{
var myPvt = ' you got me ';
TestB.prototype.setMyPvt = function(v){myPvt = v};
TestB.prototype.getMyPvt = function(){return myPvt};
}
return TestB;
})();


/* If you do this you might expect to see "you got me," but instead
you will see "do not touch." */
var tb = new TestB();
tb.talk();

tb.setMyPvt("wtf?");
tb.talk(); // still "do not touch"

....
This is why it's important that references to functions are copied,
not the functions themselves... 'this' refers to the execution context
and that can change when the function (reference) gets moved around,
but the 'var's the function references do not change from their old
scope. I've got a dirty hack to work around it, basically re-copy the
function body using 'eval' and the local privates will be used instead
of whatever ones the function was "born" with. I think I will not be
using this hack.
....

// constructor
function TestB ()
{
// ...
TestB.prototype.talk = eval('('+TestA.prototype.talk+')');
}

....
I don't know how well you could mimic Java's "protected" members,
though.

I'm wondering what you need all this *for* though.  Do you really need
what are essentially Javascript proxy classes to your PHP ones?  I'm
certainly not trying to say you don't, but even in fairly complex
projects, I've never found any need for this.

I don't know, the administrative ends of these things (grids, reports,
maps and the like) always seem to end up accumulating a bunch of js
and it seemed like a good idea for the data model to be available
through js, just to have a starting point for developers when adding
stuff. Need to make a photo uploader? Extend the generated Photo class
with some new methods. Need a google map? Wrap the functionality in
the Address class so when you decide to change to the new gmaps api,
your internal API can stay the same. That kind of thing.

It might turn out to be a terrible idea, but I figure it's worth a
shot.

-- Nick
 
N

nick

Every time I wrote "protected" in quotes in that first paragraph I
meant "privileged." Oops.

-- Nick
 
S

Scott Sauyet

Let's throw all that stuff in the constructor and see what happens.
...

this.TestA = (function(){
  // constructor
  function TestA ()
  {
    var myPvt = ' do not touch ';
    TestA.prototype.talk = function(){alert(myPvt)}
  }
  return TestA;

})();

this.TestB = (function(){
  TestB.extend(TestA);
  // constructor
  function TestB ()
  {
    var myPvt = ' you got me ';
    TestB.prototype.setMyPvt = function(v){myPvt = v};
    TestB.prototype.getMyPvt = function(){return myPvt};
  }
  return TestB;

})();

/* If you do this you might expect to see "you got me," but instead
you will see "do not touch." */
var tb = new TestB();
tb.talk();

tb.setMyPvt("wtf?");
tb.talk();            // still "do not touch"

How about if you provide the function in your base class, then
override it in your extension?

this.TestA = (function(){
// constructor
function TestA ()
{
var myPvt = ' do not touch ';
TestA.prototype.talk = function(){alert(this.getMyPvt())}
TestA.prototype.getMyPvt = function(){return myPvt};
}
return TestA;
})();

I think this would behave the way you want, but it's not very clean.

-- Scott
 
T

Thomas 'PointedEars' Lahn

Scott said:
How about if you provide the function in your base class, then
override it in your extension?

this.TestA = (function(){
// constructor
function TestA ()
{
var myPvt = ' do not touch ';
TestA.prototype.talk = function(){alert(this.getMyPvt())}
TestA.prototype.getMyPvt = function(){return myPvt};
}
return TestA;
})();

I think this would behave the way you want, but it's not very clean.

And there are no classes. Besides, the talk() prototype method does not
need to be defined in the constructor, and the getMyPvt() prototype method
needs to be defined only once.

this.TestA = (function() {
var _myPvt = ' do not touch ';

function _getMyPvt()
{
/* or hard-code the value here */
return _myPvt;
}

function TestA ()
{
/*
* buy the assignment with a type test here, or move
* it out of the constructor, if you want
*/
TestA.prototype.getMyPvt = _getMyPvt;
}

/* Assign this like getMyPvt() in the constructor if you want */
TestA.prototype.talk = function() {
window.alert(this.getMyPvt());
};

return TestA;
})();


PointedEars
 
E

err_

Scott, that's something I hadn't thought of... it's not exactly what I
want but it's better than that eval hack for sure... thanks :)
And there are no classes.  Besides, the talk() prototype method does not
need to be defined in the constructor, and the getMyPvt() prototype method
needs to be defined only once.

  this.TestA = (function() {
    var _myPvt = ' do not touch ';

    function _getMyPvt()
    {
      /* or hard-code the value here */
      return _myPvt;
    }

    function TestA ()
    {
      /*
       * buy the assignment with a type test here, or move
       * it out of the constructor, if you want
       */
      TestA.prototype.getMyPvt = _getMyPvt;
    }

    /* Assign this like getMyPvt() in the constructor if you want */
    TestA.prototype.talk = function() {
      window.alert(this.getMyPvt());
    };

    return TestA;
  })();

Pointy, (I'm sure you probably know this but for the record) the
problem with moving the privates out of the constructor is they act
sort of "static" if you put them out there... They act like private
statics, which sounds like a contradiction, but look:

this.TestA = (function() {

// private outside of ctor
var _myPvt = ' do not touch ';

// ctor
function TestA (newPvt)
{
if (newPvt !== undefined)
_myPvt = newPvt;
}

// methods here or in ctor
TestA.prototype.talk = function()
{
window.alert(this.getMyPvt());
};
TestA.prototype.getMyPvt = function()
{
return _myPvt;
};
TestA.prototype.setMyPvt = function(v)
{
_myPvt = v; return this;
};
return TestA;
})();


var foo = new TestA();

foo.talk(); // do not touch

var bar = new TestA('lalala');

bar.talk(); // lalala

foo.talk(); // lalala

var baz = new TestA();

baz.talk(); // lalala


....
-- Nick
 
T

Thomas 'PointedEars' Lahn

err_ said:

Who, error?
(I'm sure you probably know this but for the record)

Yes, I do.
the problem with moving the privates out of the constructor
is they act sort of "static" if you put them out there...
They act like private statics, which sounds like a contradiction,
but look:

ISTM that you don't know what you are talking about.
this.TestA = (function() {

// private outside of ctor
var _myPvt = ' do not touch ';

// ctor
function TestA (newPvt)
{
if (newPvt !== undefined)
_myPvt = newPvt;
}
[...]

Non sequitur. You *modified* the constructor here so that it modified the
"private" variable; my code does not do that. That leaves as the only
inherent problem with moving the prototype property (not variable)
assignment out of the constructor, that the prototype property value is not
reset on construction. That is, you exchange efficiency for a bit of
pseudo-security here ("pseudo", because the value can be overwritten later
at any time anyway)


PointedEars
 
N

nick

Who, error?

bah, google groups is a pain.
Yes, I do.


ISTM that you don't know what you are talking about.

That's okay, I'm confident that I do ;)

this.TestA = (function() {
  // private outside of ctor
  var _myPvt = ' do not touch ';
  // ctor
  function TestA (newPvt)
  {
    if (newPvt !== undefined)
      _myPvt = newPvt;
  }
[...]

Non sequitur.  You *modified* the constructor here so that it modified the
"private" variable; my code does not do that.  

I could have modified it by adding a setter to achieve the same thing.
That leaves as the only
inherent problem with moving the prototype property (not variable)
assignment out of the constructor, that the prototype property value is not
reset on construction.  

That was exactly my point -- the value of those variables is shared
between every "instance" of the "class."

this.TestA = (function() {
var _myPvt = ' do not touch ';
function _getMyPvt()
{
return _myPvt;
}
function _setMyPvt(v)
{
_myPvt = v;
}
function TestA ()
{
/*
* buy the assignment with a type test here, or move
* it out of the constructor, if you want
*/
TestA.prototype.getMyPvt = _getMyPvt;
TestA.prototype.setMyPvt = _setMyPvt;
}
/* Assign this like getMyPvt() in the constructor if you want */
TestA.prototype.talk = function() {
window.alert(this.getMyPvt());
};
return TestA;
})();


var foo = new TestA();

foo.talk();
foo.setMyPvt(" whee ");

var bar = new TestA();
bar.talk(); // "whee"

bar.setMyPvt(" whoo ");

foo.talk(); // "whoo"

....
So saying these are anything like private class properties in a
'normal' OO language is misleading. Not that you said that, of course.

-- Nick
 
D

Dmitry A. Soshnikov

Scott, that's something I hadn't thought of... it's not exactly what I
want but it's better than that eval hack for sure... thanks :)









Pointy, (I'm sure you probably know this but for the record) the
problem with moving the privates out of the constructor is they act
sort of "static" if you put them out there... They act like private
statics, which sounds like a contradiction, but look:

this.TestA = (function() {

  // private outside of ctor
  var _myPvt = ' do not touch ';

  // ctor
  function TestA (newPvt)
  {
    if (newPvt !== undefined)
      _myPvt = newPvt;
  }

  // methods here or in ctor
  TestA.prototype.talk = function()
  {
    window.alert(this.getMyPvt());
  };
  TestA.prototype.getMyPvt = function()
  {
    return _myPvt;
  };
  TestA.prototype.setMyPvt = function(v)
  {
    _myPvt = v; return this;
  };
  return TestA;

})();

var foo = new TestA();

foo.talk();  // do not touch

var bar = new TestA('lalala');

bar.talk(); // lalala

foo.talk(); // lalala

var baz = new TestA();

baz.talk(); // lalala

Anyway, described pattern won't allow to realize the complete getter/
setter for encapsulated data - it's just useful for getter, setter
from the other object will rewrite the same shared value which is in
scope chain of function allowed for both objects.

So this is just complicated way of more clear declaration of *shared*
encapsulated entities in helper execution context for creating
prototype object (but not the constructor function):

function A(x) {
this.x = x || 100;
}

A.prototype = (function () {

var _someSharedVar = 500;

function _someHelper() {
alert('internal helper: ' + _someSharedVar);
}

function method1() {
alert('method1: ' + this.x);
}

function method2() {
alert('method2: ' + this.x);
_someHelper();
}

return {
constructor: A,
method1: method1,
method2: method2
};

})();

var a = new A(10);
var b = new A(20);

a.method1(); // method1: 10
a.method2(); // method2: 10, internal helper: 500

b.method1(); // method1: 20
b.method2(); // method2: 20, internal helper: 500

/ds
 

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

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top