Thinking About Encapsulation And Direct Object References

Posted June 12, 2011 at 6:01 PM by Ben Nadel

Tags: Javascript / DHTML

After reading JavaScript Web Applications by Alex MacCaw, I have been reinvigorated in my desire to learn more about Object Oriented Programming (OOP) and Model-View-Controller (MVC) architectures; both in general and specifically in thick-client applications. I have been struggling with OOP concepts for years; and, this recent foray into JavaScript application design has reminded me just how many holes remain in my understanding of the topic. One of the first stumbling blocks that I've hit coming out of the gate was that of encapsulation and direct object references.


 
 
 

 
  
 
 
 

For my JavaScript / OOP learning platform, I have been working on a client-side application that would allow me to maintain a collection of the "girl names" that I commonly use in my code samples. In my first approach to this application, I had a girlService object that maintained an encapsulated collection of Girl instances. Then, as a convenience, I used jQuery's data() method to associate Girl instances with relevant, view-based DOM elements.

As I was building this, I suddenly realized that I was completely breaking the encapsulation of the girlService object. By supplying external references to the internally maintained collection of Girl instances, I was allowing external code to update the domain model outside of the required business logic.


 
 
 

 
 Direct object reference to persisted data break the encapsulation of that data container. 
 
 
 

As you can see in the above diagram, external code (in this case a UI Controller) was being given direct access to the internally persisted girl instances. This presented the UI controller with the ability to change the underlying domain model without having to deal with the business limitations potentially imposed by the service layer.

To fix this hole in our encapsulation, we have to be sure that our service layer returns independent references to Girl instances. That is, subsequent requests for a Girl with ID (1) return different instances of said Girl, neither of which are a direct reference to the internally persisted version.

To explore this concept, I put together some sample code that contains 3 domain model objects:

  • GirlDAO
  • GirlService
  • Girl

The GirlDAO (or Data Access Object) is the entity that handles the persisting of Girl data. It provides basic CRUD functionality - Create, Read, Update, Delete - but would, if it had to, contain other "gateway" type methods as well. Essentially this object is primarily concerned with the storage of raw data and the translation of that data into Girl entities.

NOTE: So far, in my OOP studies, I am not convinced that DAO and Gateway oriented methods need to be in different objects. To me, they don't seem dissimilar enough to warrant the separation.

The GirlService is the entity that provides the business logic to the application. That is, it acts as a necessary intermediary between the controllers and the persistence. In our sample application, this layer imposes an 18-year-old restriction on Girl instances.

The Girl is the entity that represents the girl concept. It has an ID, a name, and an age. For it to be considered valid, the name must be of a non-zero length and the age must be greater than or equal to zero. Notice that age validation of the Girl itself is different than the age validation of the GirlService - this is where it becomes especially important that our encapsulation is not violated.

Ok, now that we see our domain model, let's run some test code to see what happens:

  • <!DOCTYPE html>
  • <html>
  • <head>
  • <title>Object Collections And Encapsulation</title>
  •  
  • <!-- Linked scripts. -->
  • <script type="text/javascript" src="./jquery-1.6.1.min.js"></script>
  • <script type="text/javascript" src="./girl-service.js"></script>
  • <script type="text/javascript" src="./girl-dao.js"></script>
  • <script type="text/javascript" src="./girl.js"></script>
  •  
  • <!-- Run page. -->
  • <script type="text/javascript">
  •  
  •  
  • // Create the service object.
  • //
  • // NOTE: I am creating the DAO object as a non-referenced
  • // value in this demo because I don't want the calling
  • // context to be able to access it without going through
  • // the Service layer.
  • var girlService = new GirlService(
  • new GirlDAO()
  • );
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // Create sarah.
  • var sarah = girlService.createGirl( "Sarah", 25 );
  •  
  • console.log(
  • sarah.getID(),
  • sarah.getName(),
  • sarah.getAge()
  • );
  •  
  •  
  • // Now, try to update the sarah object directly.
  • sarah.setAge( 35 );
  •  
  •  
  • // After updateing sarah directly, re-retreive the sarah
  • // instance from the service.
  • var sarah2 = girlService.readGirl( sarah.getID() );
  •  
  • // Log the age of the newly-gotten instance.
  • console.log(
  • sarah2.getID(),
  • sarah2.getName(),
  • sarah2.getAge()
  • );
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  • console.log( "----" );
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // In this system, girls need to be 18 or older. Try to
  • // create one that is 17.
  • try {
  •  
  • // Create the teenager.
  • var teenager = girlService.createGirl( "Kit", 17 );
  •  
  • } catch( error ){
  •  
  • // Log the details for the error (this is our validation
  • // error collection).
  • console.log( error.details );
  •  
  • }
  •  
  •  
  • // Now, try to create a girl directly.
  • var teenager2 = new Girl( 0, "Kit", 17 );
  •  
  • // Log the directly-created girl.
  • console.log(
  • teenager2.getID(),
  • teenager2.getName(),
  • teenager2.getAge()
  • );
  •  
  •  
  • </script>
  • </head>
  • <body>
  • <!-- Left intentionally blank. -->
  • </body>
  • </html>

Running this code results in the following console output (which we shall discuss momentarily):

1 Sarah 25
1 Sarah 25
----
[Object { property="age", message="Age must be greater than or equal to 18."}]
0 Kit 17

This code is broken up into two areas of exploration - independent entity references and business logic validation. In the first half, we create sarah - an instance of the Girl class. Then, we change the age of the given entity reference to 35 before re-retrieving the same record and logging it to the console. Doing so results in the following console output:

1 Sarah 25
1 Sarah 25

As you can see, the change to age of the first "sarah" instance does not yield any persistant changes. That's because the entity reference returned by the service layer was not a direct reference to the persisted data; rather, it was just a representation of the persisted data. By returning independent entity references, we allow the internal data collection to be completely encapsulated by the business objects that know how to manage it.

In the second half of the code, we look at the creation of domain entities both with and without the use of the service layer (GirlService). When we try to create a 17-year-old girl using the service layer, we get get an exception that tells us:

Age must be greater than or equal to 18.

When we instantiate a 17-year-old girl directly, however, we have no problems and can log the girl as such:

0 Kit 17

The good news is, even though we can directly instantiate the 17-year-old girl entity, we have no way of persisting it or integrating it within our system. Any attempt to pass this data through to the service layer would impose the 18-year-old restriction and raise an exception. In essence, our service-oriented workflow provides a framework in which data integrity is properly maintained.

If you are curious to see the JavaScript code for the three domain objects, here it is:

NOTE: Not all aspects of this code have been tested. Only the parts in the actual demo were run.

GirlDAO (Data Access Object)

  • // Define the Girl DAO (Data Access Object) class.
  • window.GirlDAO = (function( $ ){
  •  
  •  
  • // I am the class constructor.
  • function DAO(){
  •  
  • // I am the collection of girls. These are not girl objects,
  • // but rather, girl data collections. Each girl will be
  • // indexed by the unique record ID.
  • this._girls = {};
  •  
  • // I am the auto-incrementer for the girls collection.
  • this._autoIncrementor = 0;
  •  
  • }
  •  
  •  
  • // I create a new girl record and return the updated girl
  • // instance.
  • DAO.prototype.createGirl = function( girl ){
  •  
  • // Store the new ID for this girl.
  • girl.setID( this._getNextID() );
  •  
  • // Create the new persistent record.
  • this._girls[ girl.getID() ] = {
  • id: girl.getID(),
  • name: girl.getName(),
  • age: girl.getAge()
  • };
  •  
  • // Return the update girl.
  • return( girl );
  •  
  • };
  •  
  •  
  • // I delete the girl with the given ID and return the associated
  • // girl instance.
  • DAO.prototype.deleteGirl = function( girl ){
  •  
  • // Delete the record.
  • delete( this._girls[ girl.getID() ] );
  •  
  • // Return the deleted girl.
  • return( girl );
  •  
  • };
  •  
  •  
  • // I return the next auto-incremented ID.
  • DAO.prototype._getNextID = function(){
  •  
  • // Pre-increment the index value and return it.
  • return( ++this._autoIncrementor );
  •  
  • };
  •  
  •  
  • // I return the girl object associated with the given ID.
  • DAO.prototype.readGirl = function( id ){
  •  
  • // Check to make sure the girl exists.
  • if (!this._girls.hasOwnProperty( id )){
  •  
  • // The girl record doesn't exists - retun null.
  • return( null );
  •  
  • }
  •  
  • // Get the persisted girl data.
  • var data = this._girls[ id ];
  •  
  • // Return the new girl object.
  • return(
  • new Girl(
  • data.id,
  • data.name,
  • data.age
  • )
  • );
  •  
  • };
  •  
  •  
  • // I update the record for the given girl.
  • DAO.prototype.updateGirl = function( girl ){
  •  
  • // Get the record for the given girl.
  • var record = this._girls[ girl.getID() ];
  •  
  • // Check to make sure a persisted record was found.
  • if (!record){
  •  
  • // We can't update a record that doesn't exist.
  • throw( new Error( "RecordNotFound" ) );
  •  
  • }
  •  
  • // Update the data record.
  • record.name = girl.getName();
  • record.age = girl.getAge();
  •  
  • // Return the updated girl.
  • return( girl );
  •  
  • };
  •  
  •  
  • // ------------------------------------------------------ //
  • // ------------------------------------------------------ //
  •  
  •  
  • // Return the class constructor.
  • return( DAO );
  •  
  •  
  • })( jQuery );

As you can see, the girl data is not persisted as Girl instances; rather, the data is persisted as a collection of hashes that contain the raw information.

GirlService

  • // Define the Girl Service class.
  • window.GirlService = (function( $ ){
  •  
  •  
  • // I am the class constructor.
  • function Service( girlDAO ){
  •  
  • // Store the girl DAO.
  • this._girlDAO = girlDAO;
  •  
  • }
  •  
  •  
  • // I create a girl with the given properties.
  • Service.prototype.createGirl = function( name, age ){
  •  
  • // First, we need to validate that the given properties
  • // are even valid (in a business sense).
  • var errors = this.validate( name, age );
  •  
  • // If we have errors, we need to raise an exception and
  • // pass the errors through with them.
  • if (errors.length){
  •  
  • // Create a new exception.
  • var exception = new Error( "InvalidProperties" );
  •  
  • // Attatch the errors to the exception.
  • exception.details = errors;
  •  
  • // Throw the exception.
  • throw( exception );
  •  
  • }
  •  
  • // Create a new girl object.
  • var girl = new Girl( 0, name, age );
  •  
  • // Persist the girl.
  • this._girlDAO.createGirl( girl );
  •  
  • // Return the new, persisted girl record.
  • return( girl );
  •  
  • };
  •  
  •  
  • // I load the girl with the given id.
  • Service.prototype.readGirl = function( id ){
  •  
  • // Return the girl instance.
  • return( this._girlDAO.readGirl( id ) );
  •  
  • };
  •  
  •  
  • // I validate the given girl properties.
  • Service.prototype.validate = function( name, age ){
  •  
  • // Create a new error collection.
  • var errors = [];
  •  
  • // Make sure the name is valid.
  • if (!name.length){
  •  
  • errors.push({
  • property: "name",
  • message: "Name must be greater than zero characters."
  • });
  •  
  • }
  •  
  • // Make sure the age is valid.
  • if (age < 18){
  •  
  • errors.push({
  • property: "age",
  • message: "Age must be greater than or equal to 18."
  • });
  •  
  • }
  •  
  • // Return the populated error collection.
  • return( errors );
  •  
  • };
  •  
  •  
  • // ------------------------------------------------------ //
  • // ------------------------------------------------------ //
  •  
  •  
  • // Return the class constructor.
  • return( Service );
  •  
  •  
  • })( jQuery );

Notice that the service layer has a validation method that allows the data to be validated before the Girl instance is created. This is because the Girl instance (below) is does not allow invalid "states" to be represented.

Girl

  • // Define the Girl class.
  • window.Girl = (function( $ ){
  •  
  •  
  • // I return an initialized object.
  • function Girl( id, name, age ){
  •  
  • // Set the default property values.
  • this._id = 0;
  • this._name = null;
  • this._age = null;
  •  
  • // Set the constructor properties.
  • this.setID( id );
  • this.setName( name );
  • this.setAge( age );
  •  
  • }
  •  
  •  
  • // I return the current Age property.
  • Girl.prototype.getAge = function(){
  •  
  • return( this._age );
  •  
  • };
  •  
  •  
  • // I return the current ID property.
  • Girl.prototype.getID = function(){
  •  
  • return( this._id );
  •  
  • };
  •  
  •  
  • // I return the current Name property.
  • Girl.prototype.getName = function(){
  •  
  • return( this._name );
  •  
  • };
  •  
  •  
  • // I set the new Age property.
  • Girl.prototype.setAge = function( age ){
  •  
  • // Make sure the age is valid.
  • if (age < 0){
  •  
  • throw( new Error( "InvalidProperty" ) );
  •  
  • }
  •  
  • // Store the new property value.
  • this._age = age;
  •  
  • // Return this object reference for method chaining.
  • return( this );
  •  
  • };
  •  
  •  
  • // I set the new ID property.
  • Girl.prototype.setID = function( id ){
  •  
  • // Make sure the id is valid.
  • if (id < 0){
  •  
  • throw( new Error( "InvalidProperty" ) );
  •  
  • }
  •  
  • // Store the new property value.
  • this._id = id;
  •  
  • // Return this object reference for method chaining.
  • return( this );
  •  
  • };
  •  
  •  
  • // I set the new Name property.
  • Girl.prototype.setName = function( name ){
  •  
  • // Make sure the name is valid.
  • if (name.length == 0){
  •  
  • throw( new Error( "InvalidProperty" ) );
  •  
  • }
  •  
  • // Store the new property value.
  • this._name = name;
  •  
  • // Return this object reference for method chaining.
  • return( this );
  •  
  • };
  •  
  •  
  • // ------------------------------------------------------ //
  • // ------------------------------------------------------ //
  •  
  •  
  • // Return the class constructor.
  • return( Girl );
  •  
  •  
  • })( jQuery );

Notice that each setter method enforces data validation. While my validation is not that strong, there is not way to create an "invalid" girl as an independent concept.

When it comes to Object Oriented Programming (OOP), I still have a tremendous amount to learn. And, Object Oriented Programming on the client-side, outside of the traditional request-response lifecycle, adds an additional layer of food for thought. As I'm hacking through my examples, please keep in mind that I'm "exploring" more than I am "explaining." But, as far as I can see, in order to maintain proper encapsulation, we have to be careful how we persist and return entity representations.



Reader Comments

Jun 12, 2011 at 7:03 PM // reply »
10 Comments

I haven't seen much out there dealing with OOP on the client-side with javascript, but it seems to mirror some of the stuff I have to work with from time to time with object factory/DAO/Gateway architectures on the server end.

I've also had occasion to dispense with a separate Gateway object for non-CRUD methods, and it seems logical enough for me to not inwardly cringe at the thought.


Jun 13, 2011 at 7:15 AM // reply »
13 Comments

Interesting post, nerd to Get the js app book synced up and start reading it,

Coming from a c++ then java that has "degraded" into mostly jsp and front end, I think the issues you raise are part of the reason for the bad rep that JS as a serious language has had

It is really easy to mess things up for yourself in the design / structure, and it can be a real hassle to sort back through

That said, I've seen MVC taken to absurd repetition in java/jsp use due to lack of dedicated classes or objects

(side note: oreilly has 50% off js ebook sale until tomorrow, code HALFD)


Jun 13, 2011 at 9:05 AM // reply »
7 Comments

ExtJS 4.x uses OOP & MVC out of the box now, making linkages and references much easier on the clientside. You might want to check out the data models there. (much different from 3.x)


Jun 13, 2011 at 9:06 AM // reply »
11,246 Comments

@Jason,

Yeah, exactly - they both seem to be dealing with persistence I/O. At least for something of the size I am playing with (small sample app), I don't think I would even gain any benefit from the separation of concerns.

The only reason I think I really like the separation of Service object and DAO is that it makes the naming much easier :) You can have two save() or create() or delete() methods and not have to worry about which one executes business logic and which one executes persistence... which I like.

@Atleb,

I highly recommend JavaScript Web Applications. It really did get me thinking hardcore again.

I definitely am aware that OO is problem solving technique, not a religion (and I hope to not accidentally go down the religious path). But, like a child that is learning right and wrong, I feel like I need to be more extreme so that I can understand when to be less extreme.

Since OO, in general, is a bit of a mystery to me, following a bit or a more "religious" mantra might, in the beginning, server more of a benefit in the long run. Eventually I will be able to decide which rules can be bent, which can be broken.

And, at the scale I am working with now, it's more just a fun thought experiment.

Thanks for the O'Reilly coupon code!


Jun 13, 2011 at 9:07 AM // reply »
11,246 Comments

@Dawesi,

Since I am unfamiliar with ExtJS's approach, can you relate it back to the concept of object references? I would definitely be interested to know how they deal with it?


Jun 13, 2011 at 9:48 AM // reply »
8 Comments

Back in your home turf of CF, I always design my services with several "default" methods per domain object (okay, call it a "database table"):

get{object}() takes the primary key of the table as an argument and returns a single record

save{object} takes a single-record object (for me, a Reactor record object, but because the API is clean you can swap in CF9 ORM or Transfer and it should work the same way) and persists it.

delete{object}() does what save{object} does, but with a delete action

get{object}Query() takes a structure of arguments to pass to the gateway for a getByFields() call, and returns a query containing all matching records.

The idea is manifold: this allows me an easy way to generate code using stylesheets and an xml configuration, it gives me a really simple, clean API for doing data persistence, and by extending this model I can keep my code nice and organized -- these methods go in a "base" service, and the generation process also generates a "custom" service which extends the base service and allows me to override those base methods or create new ones like get{object}ByOtherFields().

Obviously this is the CF world, where data persistence and querying is approximately 90% of my work day, but the concepts are the same -- don't allow your application logic to talk directly to the business objects/persistence layer. Instead, the service layer should have methods that handle that for you.

Good post, cool to see it in another language.


Jun 13, 2011 at 10:03 AM // reply »
11,246 Comments

@Joe,

The little bit that I've read about JavaScript frameworks like Spine.js and Backbone.js, I think they take a similar approach - the CRUD methods are part of the core "Model" class that can then be used abstractly or overridden if need-be.

I've also seen some people in ColdFusion create some dynamic "find" methods that are powered by the onMissingMethod() event handler. I think that is cool; but that level of dynamic method invocation doesn't exist in JavaScript (that I know of).

This is fun stuff to think about! Often frustrating, but always fun!


Jun 13, 2011 at 11:53 AM // reply »
13 Comments

@Ben,
i'm clearly an O'reilly discount junkie :)
currently reading Programming HTML5 Applications (another early access slated for this fall),
with Gamestorming and Building web Reputation Systems in the digital queue.

retail cost for the books over here is around $50 and up, so $9 -15 for e-versions is simply brilliant for diving into a variety of subjects


JF
Jun 13, 2011 at 12:03 PM // reply »
8 Comments

Wouldn't that make more sense if

var teenager2 = new Girl(0,"Kit", 17);

would be the one responsible for throwing the error, in which case your service layer can simply rethrow it and you've now killed two birds with one stone?


Jun 13, 2011 at 9:48 PM // reply »
11,246 Comments

@Atle,

How is that book. I was looking at it recently (on the O'Reilly website). It looked interesting.

@JF,

This is fantastic question - at the crux of this problem - and probably one that has several valid answers. When I think about this, I try to ask myself the question, "What if it had to change?"

So, in this example, age must be 18 or greater. But, what if it had to be 65 or greater? Would I need to change the concept of "what does it mean to be an" XYZ entity? And if so, does that "business rule" belong outside the entity? Meaning, does this particular constraint really apply the given entity? Or merely to the given in entity IN the given context?

I guess, it really boils down to the "context" question. In this case, we don't really know. Or rather, in this case, given what we can see, you are correct - it would make more sense to put the age validation in the entity since we don't see any need for it not to be. But, should the application scope expand to allow for entities to be used in different context, then the answer might become, No.

Sorry for the stream of consciousness - as you can probably tell, I am half thinking / half talking. This stuff is really a whole big mystery to me. If anything, I am trying to err on the side of a separation of concerns because this is all so new to me.


JF
Jun 13, 2011 at 10:09 PM // reply »
8 Comments

@Ben,

The way I usually see it is that a domain entity should never allow itself to be persisted in a invalid state for the business.

It boils down to programming defensively making no assumptions.

What if the new guy decide to bypass the service layer and use Girl directly, they would then be able to persist objects in an invalid state. Sure they're not suppose to do it, but if they do... BOOM GOES THE DYNAMITE!

If separation of concern is an issue and in this case it is I would extend the base object.

i.e. a "valid" girl can have an age that must be between 0 and a standard upper boundary.

Now if we deal with a "particular kind of girl" you can extend the base object and add more business logic in the setter for the age field.

Something like this in rough pseudo-ish code

Actress extends Girl{

public function setAge(int age){
if(arguments.age < 18)
throw;
variables.age = arguments.age;
}

}

Not sure how common it is to approach it this way. I usually see the service/bean used in languages that are tied to Java and not so much in other languages.

Not coming from a Java background I never really got into the whole idea of a bean class that can't persist itself without another layer. I prefer my beans to be in a good chili and leave them out of my workplace ;) but I guess beans can be a versatile ingredient if used properly.


Jun 13, 2011 at 10:21 PM // reply »
11,246 Comments

@JF,

Quick aside: Are you a Tosh.0 fan? He recently had a "web redemption" with the Boom Goes The Dynamite guy. Anyway, I love that show.

As far as defensive programming goes, my attempt to touch that concept was to not allow the object to be persisted directly; but rather, to have to be passed through the Service object, which would get a chance to apply constraints. So, my defensiveness was the workflow rather than the object definition.

That said, I definitely like your idea about extending the base class in order to impose addition constraints. My experience is such that I wouldn't be able to sell you on one idea vs. the other. Like a philosophy class, both sides can seem totally valid depending on who's doing the talking.

In the stuff that I've read about JavaScript frameworks, I believe they tend to side with you regarding persistence - that objects save themselves. In fact, I don't think I've seen much in the way of (in JavaScript) any kind of server / entity separation.

Right now, I am just working on getting *one* demo to work. When I'm done with that, at least I'll have a starting point from which to start refactoring.


JF
Jun 13, 2011 at 10:40 PM // reply »
8 Comments

@Ben,

No, never heard of Tosh.0. I tried checking it out but comedycentral doesn't like Canucks much and won't let me view it :(

Looks good though, I'm sure it's available elsewhere I'll have a look when I have a bit of spare time.

As far as the service/persistence thing goes I get both side of the coin. It took me a while but I can see why a service layer can be appealing in some case :) I had to get used to it when I played in some of Ray's code before.

It's definitely two very distinct approach to solving a problem and they both have their merit.

As far as OOP concept apply I'll use an example from my University teacher when he tried to explain it.

Before OOP you would say:

"Printer! Please print this document." Presumably via a PrintService.Print(doc) function.

With OOP you would ask:

"Document please print yourself" Presumable via a Print function directly in the document, you don't care how it's printed after all you just want to print it.

I think this might be why you to see the latter being used a bit more (?) So many developers come from a straight up academic background they just go with what was hammered down with 4 years of CS studies.


Jun 13, 2011 at 10:52 PM // reply »
11,246 Comments

@JF,

My problem is, I went to school for CS and didn't understand any of it at the time :D Honestly, if I could go back to school and get formally taught (again), I would probably get so much more out of it. When I was younger, I didn't know enough to be able to relate to the concepts.

Plus, I honestly don't remember learning much about OOP. I vaguely remember learning a bit about MVC architecture (in the 2 weeks one of the classes taught Java). But beyond an intro to "Data Structures", I can't say I actually recall many OO opportunities.


Jun 14, 2011 at 2:23 AM // reply »
16 Comments

@JF: A guarded setter like the example you gave is the obvious answer to the problem of keeping objects valid. However, I almost never see it in practice as a general approach to validation. Actual usage seems to be heavily biased in favour of validating at a checkpoint, e.g. when the object is persisted. I presume this is because validation can get arbitrarily expensive (e.g. when you're talking about things like uniqueness constraints), but I've never really heard that case made.

Confusing this whole issue is the fact that so many coders use the same object type for both provisional data - say data from a form that has not yet been validated - and actual domain objects. Clearly you can't have guarded setters on the former, so rather than do the obvious thing and create a separate type, people just jettision the guards.

By the way, that printer example really is a pretty poor example. I presume your point was that bogus examples like this contribute to all the bogus OO out there?


Jun 14, 2011 at 8:17 AM // reply »
148 Comments

@Ben,

My former supervisor, who was one of the most brilliant and eccentric people I've known, said that whatever they teach you in CS in college doesn't apply in the real world. So, don't worry. I wish I could get in touch with him again, but his name is a very common name and I've never been able to turn him up in my Google searches.


Jun 14, 2011 at 8:45 AM // reply »
7 Comments

Here's a quick overview of the data model in ExtJS... http://www.sencha.com/blog/ext-js-4-anatomy-of-a-model/

It would not be that hard to apply your example above to the data package, and CRUD is ready to roll as is validations with a quick config or two.

drop me a message if you have any questions.


Jun 14, 2011 at 9:18 AM // reply »
11,246 Comments

@Jaime,

The submission of form data is definitely a point at which I differ from many of the OO people that I see. To me, it never made sense to store form data directly into an object. I've tried that approach in the past and I just always had the sneaking suspicion that something was going to break. Granted, a lot of that was probably just my nervous energy :) But, it didn't feel right. After all, I don't have a domain entity yet - I just have form data.

And going back to the "uniqueness constraints" you mentioned, that to me seems like another reason that certain types of validation should take place outside of the entity. I can imagine an idealized object that knows how to "print" itself (as in the aforementioned example); but, I have trouble trying to imagine an object that validates its own uniqueness. This would appear to be a class of information that requires understanding more than the entity has access to, even in an "idealized" OO world.

@Lola,

Thanks for the reassurance :) I'm probably just romanticizing the past anyway. I definitely understand that there is going to be a difference between the philosophy of coding and the practicalities of programing; and, that a mixture of both is probably the most effective approach. I just need to figure out how it all fits together.

@Dawesi,

Thanks for the link. Looks like a good post; I'll to read that later today.


JF
Jun 14, 2011 at 10:08 AM // reply »
8 Comments

@Jaime,

I usually don't use setter validation for expensive operation, most of the time I have it for range check. It's a simple step up from the built in type checking of most languages.

Our custom framework does allow us to validate values before passing them so maybe it's why I favor that particular style at the moment. You are correct it can be a pain if you use your objects as storage and as actual valid domain object.

Most MVC platform I've seen tend to go with validating before passing it to the model which I find to be a clean approach. You do store the form data in objects, but objects that have nothing to do with your domain, once it's validated you pass it on to the model and simple guarded setters are there to prevent whoopsies as a last line of defense.


JF
Jun 14, 2011 at 10:15 AM // reply »
8 Comments

@Jaime,

Re the printer example, it's bogus sure but taught that way most of the time. At least it was a few years ago, hopefully that part of the curriculum changed.

I know it took me a good while to get out of the "OMG this isn't strict OOP therefore it's bad" mentality.


Jun 14, 2011 at 10:39 AM // reply »
5 Comments

Ben,

This might not really pertain to what you are discussing, it might just be some food for thought. Anyway, lately I've been trying to move away from an "anemic domain" model in my domain objects. Basically in an anemic domain the domain objects are just "dumb" objects that only contian properties and getters and setters for those properties and rely on other objects to process their business logic. I think its much easier to break encapsulation and much harder to maintain when you have to rely on helper classes to actually make your domain do something.

One of the basic premises of OO is that your objects SHOULD contain both data AND behavior. The way I've been programming lately is to have smart, rich domain objects that have a very limited API and rely on some composition to implement a persistance layer and even a rules engine. So for example, in a Girl class, to implement CRUD, I'd have two public methods in that class called Get() and Save() that would use some basic logic to call private methods to do their work. The Get() method would check the Girl's internal state to determine if we are loading an object from a persistance store or creating a new instance of the object. The Save would do something similar but use the state to either create a new instance, update an instance, or delete an instance. To me, calling girl.Save() is much more intuitive, reuseable, and portable than having to rely on a service layer to do actions on an object.

I reserve my service layer for helping intialize screens for editing (loading related objects and lists of objects).

Ryan


Jun 14, 2011 at 1:53 PM // reply »
11,246 Comments

@JF,

I've also heard of people using "Form Helper" objects. Basically, like what you are saying - an object that stores the form data, but is not necessarily related the domain entities. I don't have too much experience with this concept (as you can see, all of this is new to me). I am not sure I can see the use of an additional object. I would probably just put that logic in the Controller-Model interaction. But again, no real experience with this.

As far as "OMG this isn't strict OOP therefore it's bad" ... word up! I think most of my stress comes from this kind of mindset; which is clearly all based on insecurities - if I only knew more about this, I would know what is and isn't valid.

At the end of the day, I think one needs to just pick an approach and go with it. Then, refactor later. I am sure many smart people have already told me this - but I always get nervous anyway.

@Ryan,

I have also explored this kind of approach a bit as well. If I have insecurities with the service approach, I also have it with the entity-oriented approach as well. I always fear that I would be creating "God Objects" that know how to do far more than they should be responsible for.

Of course, one thing to consider is - is "validation" a behavior? I know you weren't talking about validation specifically; but, I can still see smarter, behavior-driven entities as not necessarily being able to validate their own state.

I think the saddest thing to consider is probably that so much of my applications tend to be data-driven that behavior doesn't necessarily come into play that often.

I should go back and re-think of a small sample app that has some sort of behavior.


Jun 14, 2011 at 2:11 PM // reply »
13 Comments

@Ben,

ten page history of web and js,
thirty page summary og JS Patterns (more or less ;D )
ten page jQ overview

twenty page on ExtJS - interesting since I haven't really tested it

then 30 on testing (good so far), before the "real" book begins - local storage, manifest, webworkers (70 p total)

i'll post back when finished the main part as well - but must admit to being a bit distracted with the fusion table api from google atm (just loving using jsbin for out of office dev testing)


Jun 14, 2011 at 3:58 PM // reply »
5 Comments

@Ben,

For validation I typically implement a rules engine. So I have rules classes for my domain entities that are responsible for specific business rules and validation, these rules classes can use the persistence layer to validate data if needed. This way I can maximize my code reuse between projects and implement client specific rules and validations in my rules classes, which only have one public facing method called validate().

As for object composition, I think that's what makes OOP so much fun. Theres a million "right" ways to do this stuff and everyone has an opinion. Typically I do what "feels" right to me (which is constantly changing the I read and learn). My approach could feel completely wrong to a different developer. Someone who is committed to a service/gateway type of architecture would prob tell me I've lost my mind.


Jun 20, 2011 at 10:50 PM // reply »
11,246 Comments

@Atle,

Sounds interesting. I know very little about ExtJS... and only the tiniest bit more about testing. Also, I had not heard of Fusion Tables before. Just Google'd it (ironic!). Seems interesting. I'll have to check that at as well.

@Ryan,

I've seen some really great examples of validation reuse. From what I've seen in Bob Silverberg's ValidateThis framework, I think he is able to reuse some validation code even between the server and the client, which is pretty awesome.

I'll definitely agree with the idea that our ideas of "what's best" are always changing. Who among us hasn't look at code that we wrote even 6 months ago thought, "What the heck was I thinking" :D Part of what makes computer science such an intensely beautiful area is that the more we learn, the more elegant everything becomes. I love this stuff!



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 23, 2013 at 6:06 PM
The Girl Who Broke My Heart, And Made Me A Better Person
Good day,ladies and gentle men, my name is Dr AMADI the great spell caster in Africa, i have help so many people for different kind of problems,who say there is no solution to problems on earth, that ... read »
May 23, 2013 at 4:26 PM
ColdFusion QueryAppend( qOne, qTwo )
@Heather, Glad people are still getting value out of this! ... read »
May 23, 2013 at 3:49 PM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, I meant the code at the bottom (not the video). I did try to experiment with an intermediary variable, like: value = users.id[ i ]; arrayContains( userIDs, value ); ... but t ... read »
May 23, 2013 at 11:06 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Are you talking about As Number: YES As String: YES As Java: YES? If so, that's with 3 different ways of referencing the constant 1, not users.id[1]. Query object references(*) are what seem ... read »
May 23, 2013 at 9:55 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Dan, According to the CF Admin, I'm running Java "1.6.0_45". As far as the DB column, in the database it's an INT. I'll see if I can dig into what CF sees it as. @WebManWalking, But h ... read »
May 23, 2013 at 9:49 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, I think the problem is that we're used to loose typing in ColdFusion, like JavaScript. If a value is a number but it's needed in an expression to be a string, noooo problem. I've encountered ... read »
May 23, 2013 at 9:47 AM
ColdFusion QueryAppend( qOne, qTwo )
You rock! Thank you, thank you, thank you!!! ... read »
May 23, 2013 at 5:19 AM
Ask Ben: Print Part Of A Web Page With jQuery
How to print also the background color of table cells and table lines ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools