I think I finally have a grasp on the model-view-controller pattern--no easy feat for me, thanks largely to scouring the Coldbox wiki, which, while spotty in some places, is very rich in others.
But what about lateral communications? Guessing from the way Hibernate works (according to the demos from dev week, as well as the online documentation), models can *have* references to other models, which would then link back to the parent as well. That makes sense from a relational database standpoint.
I am assuming, for the sake of my own sanity, that services should be able to *use* functionality in other services. For our sporting application, races are sometimes just races, but sometimes races have media, and our report is an abbreviated race listing with links to media. So I have a media service which handles the at least 5 different ways to get a list of media, but it uses the race service to add a race and append related media to it, if that makes sense. Because we use stored procedures exclusively, the media service retrieves both the races and associated media in one shot.
In a related note, I have recently tried to implement MVC for an unrelated jquery-driven app that I am writing, and have found the need for a main controller which then talks to separate handlers. In this world, the handlers "handle" specific interactions with the page elements (rendered by the views, of course) and the client, and I have segregated them according to top-level functionality. On the other hand, the models are abstract representations that know nothing about the page elements nor the client.
Hopefully, this sort of lateral communication, particularly across services and controllers, is not compensation for some glaring design flaw that I'm missing. But it works really well, and thinking it through, I cannot see how it violates any core OOP principles--in fact it seems to reinforce them.
Geek stuff, including, but not limited to, Coldfusion, Javascript, AJAX, CSS, HTML, and SQL
Tuesday, November 22, 2011
Tuesday, October 18, 2011
Evolution of a data model
So I've established in previous posts how sometimes it's prudent for me to use collections of objects in our application. In my attempt to figure out the right relationship between service/data/value objects, I have went through far too many way too embarrassing designs to belabor; the fortunate thing being that none of them made it to production.
In my 1st approach, I wasn't really thinking about having a service layer. My desire was that app should just be able to ask for a property, and get a guaranteed response--it should never have to know what method to call in order to populate that object ahead of time. This was the frame of mind that I was trying to codify in my "Iterating Business Object" post. This meant that the value object itself knew how to get its own value if one hadn't already been defined.
But I have abandoned that and decided to use the more conventional service model approach. The discussion about "don't come to me, I'll come to you," though only vaguely related kind of convinced me that this approach was an acceptable one. Admittedly, some small part of my mind still thinks the former approach would be really cool, but being a noob, I defer to the currently accepted best practice here.
So I mentioned how we have 100 or so products, and about 3 major subjects, and that when you provide a subject to a product, you end up with a report. The thing about reports is that they also heavily cross-link to other reports (a la, "want to more details? try this report..."). And, of course, part of the search interface involves the user choosing a report for the subject. So in a single request cycle, we are dealing with details about more than one product (but only ever one report--we need to build links to the other reports, but don't need the actual handler, data, and view(s) associated with it).
So this has become my congealed approach to handling value objects, services, and collections. I have a service object, which is a singleton, and has a bunch of methods that talk to the data layer (singleton) to get product details, populate those into product objects (request scope, service uses the "provider" concept to get these objects), and add each product object to a collection (another provided object in the request scope). The service always checks whether the product is in the collection first before adding it. The implementation doesn't ever talk to the collection (shouldn't anyway), it talks to the service which returns either a single object or an array of objects.
I believe this is fairly standard, and it is working really well. We have about 6 different ways to get products (by id, by the report URL, but subject types...). To facilitate this in the collection, whenever I add a product, I index it against the necessary keys for getting it later. This way, I don't have to loop through the array holding all the products each time. The index is just each value in the key and the corresponding product element index in the collection array.
The problem I ran up against today was the need to iterate over the collection in groups. Obviously, this is really easy with queries. Additionally, if it ever becomes necessary to retrieve products by a different index, then I have to create a new access method, as well as a method that will create/populate the index when its added. This seems a bit too procedural. What'd be really cool is a generic getProductBy(property, value) method, but then I need to have an index on *every* property! And what happens when I need to get products by more than 1 criteria, which the way our business works is bound to happen sooner or later?
So super-awesome thing: query column values can be set to objects =0 So I haven't actually built it out yet, but maybe the internal storage mechanism for the collection could actually be a query, instead of an array--after all, queries are generally super-efficient in Coldfusion. Each property on the object would be a column in the query, with an additional column holding the object itself. This makes it so simple to get only the products needed from the collection, even if there's more than one criteria. It also makes it easy to order the collection, which can additionally facilitate grouping. The grouping itself would still need to be done in the implementation (e.g. by comparing the current object to the previous one), and the all the properties about the value object would be stored in 2 places (in the object itself as accessors, and in the collection as a query). Once the products where selected, the column holding the object could be converted to an array via either listToArray(valueList()) or a simple loop...
I'm stoked. Hope this works out well. The only thing left then is some sort of consistent pattern for providing micro-data sets (which usually end up being children of larger value objects) without needing to build out the entire service/collection/value-object model. And related to that, I continue to run into instances where these micro data-sets have the same properties, but are accessed differently/separately in the application. Maybe they do just need to be built into collections of objects... ugh... that seems like overkill.
In my 1st approach, I wasn't really thinking about having a service layer. My desire was that app should just be able to ask for a property, and get a guaranteed response--it should never have to know what method to call in order to populate that object ahead of time. This was the frame of mind that I was trying to codify in my "Iterating Business Object" post. This meant that the value object itself knew how to get its own value if one hadn't already been defined.
But I have abandoned that and decided to use the more conventional service model approach. The discussion about "don't come to me, I'll come to you," though only vaguely related kind of convinced me that this approach was an acceptable one. Admittedly, some small part of my mind still thinks the former approach would be really cool, but being a noob, I defer to the currently accepted best practice here.
So I mentioned how we have 100 or so products, and about 3 major subjects, and that when you provide a subject to a product, you end up with a report. The thing about reports is that they also heavily cross-link to other reports (a la, "want to more details? try this report..."). And, of course, part of the search interface involves the user choosing a report for the subject. So in a single request cycle, we are dealing with details about more than one product (but only ever one report--we need to build links to the other reports, but don't need the actual handler, data, and view(s) associated with it).
So this has become my congealed approach to handling value objects, services, and collections. I have a service object, which is a singleton, and has a bunch of methods that talk to the data layer (singleton) to get product details, populate those into product objects (request scope, service uses the "provider" concept to get these objects), and add each product object to a collection (another provided object in the request scope). The service always checks whether the product is in the collection first before adding it. The implementation doesn't ever talk to the collection (shouldn't anyway), it talks to the service which returns either a single object or an array of objects.
I believe this is fairly standard, and it is working really well. We have about 6 different ways to get products (by id, by the report URL, but subject types...). To facilitate this in the collection, whenever I add a product, I index it against the necessary keys for getting it later. This way, I don't have to loop through the array holding all the products each time. The index is just each value in the key and the corresponding product element index in the collection array.
The problem I ran up against today was the need to iterate over the collection in groups. Obviously, this is really easy with queries. Additionally, if it ever becomes necessary to retrieve products by a different index, then I have to create a new access method, as well as a method that will create/populate the index when its added. This seems a bit too procedural. What'd be really cool is a generic getProductBy(property, value) method, but then I need to have an index on *every* property! And what happens when I need to get products by more than 1 criteria, which the way our business works is bound to happen sooner or later?
So super-awesome thing: query column values can be set to objects =0 So I haven't actually built it out yet, but maybe the internal storage mechanism for the collection could actually be a query, instead of an array--after all, queries are generally super-efficient in Coldfusion. Each property on the object would be a column in the query, with an additional column holding the object itself. This makes it so simple to get only the products needed from the collection, even if there's more than one criteria. It also makes it easy to order the collection, which can additionally facilitate grouping. The grouping itself would still need to be done in the implementation (e.g. by comparing the current object to the previous one), and the all the properties about the value object would be stored in 2 places (in the object itself as accessors, and in the collection as a query). Once the products where selected, the column holding the object could be converted to an array via either listToArray(valueList()) or a simple loop...
I'm stoked. Hope this works out well. The only thing left then is some sort of consistent pattern for providing micro-data sets (which usually end up being children of larger value objects) without needing to build out the entire service/collection/value-object model. And related to that, I continue to run into instances where these micro data-sets have the same properties, but are accessed differently/separately in the application. Maybe they do just need to be built into collections of objects... ugh... that seems like overkill.
Sunday, October 16, 2011
Fear of change... or reasonable skepticism
Ben Nadel offers some comfort, from 2008, and a very articulate, substantive discussion regarding the value of OOP in Coldfusion. I'm not up-to-date on where his mindset has evovled to in the past 3 years, but some very knowledgeable people have spoken up against pure OOP, and I feel like the distinction between data display versus behaviors hits the nail on the head.
http://www.bennadel.com/blog/1385-Object-Oriented-Programming-And-ColdFusion-What-s-The-Point-.htm
It feels good to have concrete validation of what I have been so dyspepsic over, instead of feeling like a boy crying wolf all the time.
But how to integrate the two worlds? Recently, I've found that working with value objects can do some good, not only in instances where the data has associated behaviors either, but also when the models are used in various places throughout the site, and you want one consistent access mechanism, but are not using the same data access routines (i.e. stored procedure calls) to populate the value objects each time.
We work on a reporting site for a "sporting" organization. You can build reports based on different players in the sport. So taking to heart the concept of encapsulate what varies (I think that's how it goes), a report is an abstract concept that has an abstract concept of subject. We have 3 main types of subjects currently in our app, and about 100 or so different reports. People can build lists of favorite subjects in the sport, but they don't always know the specifics of the player, and may need to distinguish between one or more player who share the same name or other details... So there is a search interface that might return more than one subject. So that is at least 3 different places where I need to deal with subjects in the application: reports, search results, and favorites lists.
In this case, setting up an interface that represents what all the subjects have in common, which isn't much (an ID, a subject type, a subject name/means of identification), allows me to create a contract between the lower, less specialized/more generalized parts of the site and the specific details of both a given subject and a report. A page is always a page, but if a page is a report, then I know I have access to a subject and to that subjects type, ID, and name--onrequeststart and the site header doesn't any longer have to determine who the subject is based on the what report is being requested and build a name for each different subject based on the various pieces of data needed for identifying it.
Additionally, when I populate a subject for a report, 99% of the data is the same as when I populate it for a favorites list or for search results, but sometimes there is more or less data about the subject depending on the context. Additionally, since we try to limit trips to the database, sprocs might be composites of more than one sproc, in which I return the base data, as well as the stuff unique to this context all in one shot. Having a SubjectService provides one common interface to populating the subject value objects with as much, or as little data as needed.
So all in all, OOP has worked out exceptionally well for me in the last few months that I have really started trying to use it based on best practices and design conventions. But last week, for some reason I started to really take it all too far and try to push everything into value objects, because, well, everything should be done consistently. Truth be told, starting out with this stuff, when an approach isn't sitting right with me, instead of trusting my 12 years of experience as a programmer, I figure I must be doing something wrong, not getting it, etc... Then I do a 180 and think "no, I'm not going to let all this new-fangled shiny codes the kids are into nowadays undercut my august skills." Anyway, seeing experts in the community struggle with the same thing is reassuring.
http://www.bennadel.com/blog/1385-Object-Oriented-Programming-And-ColdFusion-What-s-The-Point-.htm
It feels good to have concrete validation of what I have been so dyspepsic over, instead of feeling like a boy crying wolf all the time.
But how to integrate the two worlds? Recently, I've found that working with value objects can do some good, not only in instances where the data has associated behaviors either, but also when the models are used in various places throughout the site, and you want one consistent access mechanism, but are not using the same data access routines (i.e. stored procedure calls) to populate the value objects each time.
We work on a reporting site for a "sporting" organization. You can build reports based on different players in the sport. So taking to heart the concept of encapsulate what varies (I think that's how it goes), a report is an abstract concept that has an abstract concept of subject. We have 3 main types of subjects currently in our app, and about 100 or so different reports. People can build lists of favorite subjects in the sport, but they don't always know the specifics of the player, and may need to distinguish between one or more player who share the same name or other details... So there is a search interface that might return more than one subject. So that is at least 3 different places where I need to deal with subjects in the application: reports, search results, and favorites lists.
In this case, setting up an interface that represents what all the subjects have in common, which isn't much (an ID, a subject type, a subject name/means of identification), allows me to create a contract between the lower, less specialized/more generalized parts of the site and the specific details of both a given subject and a report. A page is always a page, but if a page is a report, then I know I have access to a subject and to that subjects type, ID, and name--onrequeststart and the site header doesn't any longer have to determine who the subject is based on the what report is being requested and build a name for each different subject based on the various pieces of data needed for identifying it.
Additionally, when I populate a subject for a report, 99% of the data is the same as when I populate it for a favorites list or for search results, but sometimes there is more or less data about the subject depending on the context. Additionally, since we try to limit trips to the database, sprocs might be composites of more than one sproc, in which I return the base data, as well as the stuff unique to this context all in one shot. Having a SubjectService provides one common interface to populating the subject value objects with as much, or as little data as needed.
So all in all, OOP has worked out exceptionally well for me in the last few months that I have really started trying to use it based on best practices and design conventions. But last week, for some reason I started to really take it all too far and try to push everything into value objects, because, well, everything should be done consistently. Truth be told, starting out with this stuff, when an approach isn't sitting right with me, instead of trusting my 12 years of experience as a programmer, I figure I must be doing something wrong, not getting it, etc... Then I do a 180 and think "no, I'm not going to let all this new-fangled shiny codes the kids are into nowadays undercut my august skills." Anyway, seeing experts in the community struggle with the same thing is reassuring.
Saturday, October 15, 2011
Enums redux and fear of change
The more I refactor the site to use objects, the more I find areas where enums would be handy, but are just too much overhead for the pay-off. When will we get custom data-types without cfc creation in Coldfusion?
Also, my past posts seem cynical. I am, by nature, cynical of programming and of the http protocol. It is strangely encouraging to know how well the world can operate on concepts so hastily developed and deployed in so many ways, but depressing too. I love learning new things, but am wary of getting excited over shiny new objects that lead to following someone off a cliff. Also learning is something of a war, after all, isn't it? Rewiring our brains, shedding our former selves, and all that jazz...
I have been much more aware lately that the world moves too fast for my tastes, at least the one I'm part of. It's not speed itself that bothers me, but the lack of means and desire to do due diligence. I am prone to inertia, and I try to fight that lately, but I dread the moment when this internal war backfires. My code is much more error-prone lately, understandable when learning something new, but I don't know how much sympathy the business will have...
My code worked before, dammit. It was fast, it was organized (albeit according to a self-made structure that existed only in my head), it was secure, and it was error-free. I love the promise of structure and shared understanding and self-documentation that OOP brings to the table: a place for everything and everything in its place. But I do not like the pressure I feel to implement it, which comes most likely from myself, but sometimes feels like its the whole community ganging up on me and my outdated ways, and I do not like the absence of a "junk drawer" solution, where things that don't fit or make sense can be stuffed. Also, the sort of artificial imposition of order via proliferate hoop-jumping contributes to this feeling that I'm standing on a house of cards, or at least my job is.
I am speaking in hyperbole for the most part, trying to put this shapeless surliness into perspective...
Also, my past posts seem cynical. I am, by nature, cynical of programming and of the http protocol. It is strangely encouraging to know how well the world can operate on concepts so hastily developed and deployed in so many ways, but depressing too. I love learning new things, but am wary of getting excited over shiny new objects that lead to following someone off a cliff. Also learning is something of a war, after all, isn't it? Rewiring our brains, shedding our former selves, and all that jazz...
I have been much more aware lately that the world moves too fast for my tastes, at least the one I'm part of. It's not speed itself that bothers me, but the lack of means and desire to do due diligence. I am prone to inertia, and I try to fight that lately, but I dread the moment when this internal war backfires. My code is much more error-prone lately, understandable when learning something new, but I don't know how much sympathy the business will have...
My code worked before, dammit. It was fast, it was organized (albeit according to a self-made structure that existed only in my head), it was secure, and it was error-free. I love the promise of structure and shared understanding and self-documentation that OOP brings to the table: a place for everything and everything in its place. But I do not like the pressure I feel to implement it, which comes most likely from myself, but sometimes feels like its the whole community ganging up on me and my outdated ways, and I do not like the absence of a "junk drawer" solution, where things that don't fit or make sense can be stuffed. Also, the sort of artificial imposition of order via proliferate hoop-jumping contributes to this feeling that I'm standing on a house of cards, or at least my job is.
I am speaking in hyperbole for the most part, trying to put this shapeless surliness into perspective...
Iterating Business Objects - Redux
After extensive consideration (perhaps not of that high a caliber, but who needs quality when you have quantity...), we decided that IBOs are not the right answer for us, and that true value objects will work just fine. I still haven't run any load testing, so it remains to be seen.
I find it annoying that small sets of data should still be modeled, for posterity, as VOs. The other thing is that sometimes the business is just messy. For instance, an individual can have a number of different licenses. They can only have a single of one sort of license, but multiples of other licenses. We treat the former license differently display-wise too, asking for it specifically by type, not merely outputting it in a standardized grid or anything as we handle the latter licenses. Additionally, there may be a historical record of actions taken when the individual was serving in the capacity of that license, however they may no longer hold the license, and we do not care about the license details any longer, but we do care about the actions taken, and the actions taken are each their own value object because they in no way share attributes, but they only have about 5 properties...
I assume there is some better way to do this, and that trying to impose value objects on micro data sets is self-defeating. It seems kind of ridiculous that OOP needs to be so rigid, and it probably isn't, but my encounters with it so far imply that. I am leery of readily-accepting mob-mentality borne of not fully understanding something but never wanting to question the authority of the guru spouting nonsense... That is probably too harsh, but I'm just saying, I'll cut you.
I find it annoying that small sets of data should still be modeled, for posterity, as VOs. The other thing is that sometimes the business is just messy. For instance, an individual can have a number of different licenses. They can only have a single of one sort of license, but multiples of other licenses. We treat the former license differently display-wise too, asking for it specifically by type, not merely outputting it in a standardized grid or anything as we handle the latter licenses. Additionally, there may be a historical record of actions taken when the individual was serving in the capacity of that license, however they may no longer hold the license, and we do not care about the license details any longer, but we do care about the actions taken, and the actions taken are each their own value object because they in no way share attributes, but they only have about 5 properties...
I assume there is some better way to do this, and that trying to impose value objects on micro data sets is self-defeating. It seems kind of ridiculous that OOP needs to be so rigid, and it probably isn't, but my encounters with it so far imply that. I am leery of readily-accepting mob-mentality borne of not fully understanding something but never wanting to question the authority of the guru spouting nonsense... That is probably too harsh, but I'm just saying, I'll cut you.
Saturday, September 17, 2011
Can you ever really have two loves?
Listened to the #cfdevweek recording on the ORM/Hibernate in CF9 presentation by Bob Silverberg today. I have a lot of questions/reservations about how the interaction with the database is implemented.
Fundamentally, I guess it comes down to whether or not we ought to be writing ad-hoc queries or using stored procedures... As long as I have been a CF developer, I have had to talk to a database (12 years and running). In that time, I have slowly and painfully grown adept at optimizing SQL. Our shop is data heavy and it is not just my data, but data shared among many interconnected and complex systems and organizations. In my 3 years employ, I am a full convert to stored procedures for efficiency (reduce trips to the database server, one of the costliest transactions to a web request; stored procedures are compiled and execute quicker than ad-hoc queries in general), security (tight data typing, ability to lock down permissions), separation of concerns (Coldfusion should contain web server code, the database contains the SQL code), encapsulation, etc...
Anyway, I may not be making my case very articulately, but I am worried that there is now going to be pressure from the community to use ad-hoc queries. Hopefully, ORM will work with sprocs as well. Guess I should have asked that...
Fundamentally, I guess it comes down to whether or not we ought to be writing ad-hoc queries or using stored procedures... As long as I have been a CF developer, I have had to talk to a database (12 years and running). In that time, I have slowly and painfully grown adept at optimizing SQL. Our shop is data heavy and it is not just my data, but data shared among many interconnected and complex systems and organizations. In my 3 years employ, I am a full convert to stored procedures for efficiency (reduce trips to the database server, one of the costliest transactions to a web request; stored procedures are compiled and execute quicker than ad-hoc queries in general), security (tight data typing, ability to lock down permissions), separation of concerns (Coldfusion should contain web server code, the database contains the SQL code), encapsulation, etc...
Anyway, I may not be making my case very articulately, but I am worried that there is now going to be pressure from the community to use ad-hoc queries. Hopefully, ORM will work with sprocs as well. Guess I should have asked that...
Wednesday, September 7, 2011
Refactoring Objects, or the Woes of the OOP Impaired Through a Series of Mixed Metaphors
I'd like to enter an official objection to the OOP review board against the principle that objects be open to extension and closed for modification.
This may be great for Object-Oriented Gurus who feel they have mastered "architecting" OOP. For those of us learning as we go, who are still under pressure to produce production-ready products, and want to leverage what we know, but what we know may contain glaring holes, the process is a lot more grueling: refactor, refactor, refactor seems to be the rule.
Every time, if you're lucky enough, you learn something new, and hopefully do not repeat the same mistakes next time, but no amount of pedagogy is going to help you predict problems and proactively avoid them--at least not me: I have to hit my head and fall in the hole and turn over the can of gas near the open flame an estimated 300 times before I learn my lesson. That's anecdotal, of course, but 300 times seems like a really good ballpark. But I guess that's a testament to learning, after all--it is not a very clean process, you have to get your hands dirty and make some really awful mistakes; otherwise, you end being too precious to take down from the shelf...
Maybe we can argue that I just don't have the OOP gene, or that my OOP gene is somehow deficient, and I should leave the cooking to those who know better, but until I find a new shiny object to steal my attention, my days are a comic tragedy of ponder, ponder, ponder, refactor, refactor, refactor, implement, implement, implement, run up against catastrophic design flaws, rinse, repeat.
This may be great for Object-Oriented Gurus who feel they have mastered "architecting" OOP. For those of us learning as we go, who are still under pressure to produce production-ready products, and want to leverage what we know, but what we know may contain glaring holes, the process is a lot more grueling: refactor, refactor, refactor seems to be the rule.
Every time, if you're lucky enough, you learn something new, and hopefully do not repeat the same mistakes next time, but no amount of pedagogy is going to help you predict problems and proactively avoid them--at least not me: I have to hit my head and fall in the hole and turn over the can of gas near the open flame an estimated 300 times before I learn my lesson. That's anecdotal, of course, but 300 times seems like a really good ballpark. But I guess that's a testament to learning, after all--it is not a very clean process, you have to get your hands dirty and make some really awful mistakes; otherwise, you end being too precious to take down from the shelf...
Maybe we can argue that I just don't have the OOP gene, or that my OOP gene is somehow deficient, and I should leave the cooking to those who know better, but until I find a new shiny object to steal my attention, my days are a comic tragedy of ponder, ponder, ponder, refactor, refactor, refactor, implement, implement, implement, run up against catastrophic design flaws, rinse, repeat.
Monday, September 5, 2011
Iterating Business Objects
I loved the idea of the iterating business object when I first encountered it. I'm hung up right now on this scenario:
So I assume here that Products is a service that sets the index and returns the IBO object. Unless I'm mistaken all 4 of these outputs would return the name and description for product2.
I think this is a real-world example: say I have a collection of 10 products, and I want to compare only 2 of them... There are ways to code around this, once you know it will happen, but my biggest concern is that having Products.GetProductByID(1) return the IBO, which is just a shell that defines a record, until it has an index, is misleading, as we would naturally assume it to always return the data for product1 when in fact it just returns the data for the current product.
Something like this is less natural, but I'm considering whether it isn't more "self-evident" what is going to happen:
Obviously, this is just an example that could be further abstracted to handle any collection, not just Products.
There is a second reason that I like this, as clunky as it seems. I can "late-bind" properties that I might or might not need, to avoid storing too much data up-front; so, when GetProductProperty saw that "description" was not yet populated, it could look at a map that tells it how to get description.
I'm toying the notion of the following Objects:
- ProductMap - tells me which properties will be available in this IBO, and which methods are responsible for getting those properties.
- Collection - the "transformed" data, stored as a query or structure...
- Iterator - provides the ability to move/loop through a collection
- Service - methods to locate the index of a record, as well as methods to populate and retrieve properties on the record. It transforms the data as it gets it, using instruction from the ProductMap.
product1 = Products.GetProductByID(1);
product2 = Products.GetProductByID(2);
WriteOutout(product1.Name);
WriteOutout(product2.Name);
WriteOutout(product1.Description);
WriteOutout(product2.Description);
So I assume here that Products is a service that sets the index and returns the IBO object. Unless I'm mistaken all 4 of these outputs would return the name and description for product2.
I think this is a real-world example: say I have a collection of 10 products, and I want to compare only 2 of them... There are ways to code around this, once you know it will happen, but my biggest concern is that having Products.GetProductByID(1) return the IBO, which is just a shell that defines a record, until it has an index, is misleading, as we would naturally assume it to always return the data for product1 when in fact it just returns the data for the current product.
Something like this is less natural, but I'm considering whether it isn't more "self-evident" what is going to happen:
product1 = products.getProductIndexByID(1);
product2 = products.getProductIndexByID(2);
WriteOutput(Products.GetProductProperty(product1, "name"));
WriteOutput(Products.GetProductProperty(product2, "name"));
WriteOutput(Products.GetProductProperty(product1, "description"));
WriteOutput(Products.GetProductProperty(product2, "description"));
Obviously, this is just an example that could be further abstracted to handle any collection, not just Products.
There is a second reason that I like this, as clunky as it seems. I can "late-bind" properties that I might or might not need, to avoid storing too much data up-front; so, when GetProductProperty saw that "description" was not yet populated, it could look at a map that tells it how to get description.
I'm toying the notion of the following Objects:
- ProductMap - tells me which properties will be available in this IBO, and which methods are responsible for getting those properties.
- Collection - the "transformed" data, stored as a query or structure...
- Iterator - provides the ability to move/loop through a collection
- Service - methods to locate the index of a record, as well as methods to populate and retrieve properties on the record. It transforms the data as it gets it, using instruction from the ProductMap.
Thursday, September 1, 2011
Coldfusion Enum "Data Types"
I really wish there was some way to create a "virtual object" in Coldfusion, or else to specify more than object paths as custom type in arguments...
My coworker was talking about enums a few weeks ago, and I blew him off, but then I cracked the Gang of Four book, and it is so dense I started to try to code the samples so I could understand things better. And right away they start with an enum:
And so out of sheer ADD, I started puzzling over how to do something like this in Coldfusion. Basically, to be able to say this argument wants an integer limited to the following values, and those values will mean this. Obviously, merely using the generic "numeric" type and then validating inside the function is neither self-documenting nor reusable.
Best I've come up with so far is this:
Room.SetSide is defined:
Unfortunately, this requires a component for every single enum (packages.utils.enums.Direction). It also requires that the type/name for each enum item (North, South, East, West) is that cfc, which means instantiating 4 objects, as far as I can figure (reusing the object creates lots of reference problems).
When originally creating the packages.utils.enums.Direction cfc, you just have to provide a list (THIS.Definition = "North,East,South,West"). EnumLibrary::Enum transforms each of those list items into members containing the enum object (enum.North = CreateObject("component", "packages.utils.enums.Direction").Init()).
Obviously, the enums library can be stored in server scope. And the enums don't have to be created before they are needed, but will then persist in the library as well. But that could mean a lot of objects sitting in memory.
And for the week of work and effort exploring various dead-ends until arriving at this solution, I still can't think of a circumstance where my life would have been so much easier for having enums...
My coworker was talking about enums a few weeks ago, and I blew him off, but then I cracked the Gang of Four book, and it is so dense I started to try to code the samples so I could understand things better. And right away they start with an enum:
enum Direction{North, South, East, West}
And so out of sheer ADD, I started puzzling over how to do something like this in Coldfusion. Basically, to be able to say this argument wants an integer limited to the following values, and those values will mean this. Obviously, merely using the generic "numeric" type and then validating inside the function is neither self-documenting nor reusable.
Best I've come up with so far is this:
Enums = CreateObject("component", "packages.utils.enums.EnumLibrary").Init();
Direction = Enums.Enum("packages.utils.enums.Direction");
Room = CreateObject("component", "packages.maze.Room").Init();
Permitted values for Direction: #Direction.PermittedValues#.
Room.SetSide(Direction.North, "Wall");
Room.SetSide(Direction.East, "Door");
Room.SetSide(Direction.South, "Wall");
Room.SetSide(Direction.West, "Door");
Side #Direction.North.Index# - #Direction.North.Value# #Room.GetSide(Direction.North)#;
Room.SetSide is defined:
function SetSide(packages.utils.enums.Direction, String);
Unfortunately, this requires a component for every single enum (packages.utils.enums.Direction). It also requires that the type/name for each enum item (North, South, East, West) is that cfc, which means instantiating 4 objects, as far as I can figure (reusing the object creates lots of reference problems).
When originally creating the packages.utils.enums.Direction cfc, you just have to provide a list (THIS.Definition = "North,East,South,West"). EnumLibrary::Enum transforms each of those list items into members containing the enum object (enum.North = CreateObject("component", "packages.utils.enums.Direction").Init()).
Obviously, the enums library can be stored in server scope. And the enums don't have to be created before they are needed, but will then persist in the library as well. But that could mean a lot of objects sitting in memory.
And for the week of work and effort exploring various dead-ends until arriving at this solution, I still can't think of a circumstance where my life would have been so much easier for having enums...
Subscribe to:
Posts (Atom)