It is suggested by some (such as CantEncapsulateLinks and FundamentalFlawsInProceduralDesigns) that RDBMS data is "global", but accessing similar information through classes is not "global". A class interface to an entity is just as global as an entity in a database from a developer's perspective. If there are techniques to limit which file or user can use which class or method, those are specific to your language or system, and not an inherent property of OOP itself. Class interfaces are essentially as global as any database entity through a database interface (such as SQL).
- Tables are a giant collection of global data. A table can't be compared to an object because an object can carry their rules with them, objects are behavioral entities. Tables can too in the form of triggers, the difference of course is in the expressibility of the language. An object allows full encapsulation and protection of it's data, a table doesn't. It's not about security, it's about encapsulation and enforcement of business rules. That's why a table is a big global variable while and object isn't. It's quite easy with an object model to ensure invalid items can't be placed into an order no matter how complex the rules, the same cannot be said for and order table and item table unless you try and encode all the business rules into triggers, which is damn near impossible because SQL doesn't do business rules anywhere near as well as a real language. {REFACTORING OF THIS GOING ON BELOW}
- If you could easily encode all possible business rules into triggers, then the schema would be equivalent to an object-oriented domain model in many ways. Each table could be thought of as an aggregate repository for that type, you may already think of it this way. The problem is of course business rules. SQL works well with simple constraints and simple rules, but it simply can't express the complex rules that real programming languages express. If you could move tables themselves into memory, and attach rules to them with a real language to enforce their integrity, then you'd have objects, and that's all our objects models do.
- You are right. They are similar. However, if we factor out ram/disk dichotomies and stupid Oracle features, it all boils down to standards versus lack of standards. (WhenAreStandardsRestrictive). Relational provides standards for state and link management, OO does not. (See CollectionOrientedVerbs and InterfaceFactoring.) Each class can reinvent the wheel in different ways that ignore relational operators and rules or any other consensus rules. "Encapsulation" usually implies that each "capsule" is relatively independent, and independence and consistency are often at odds. It all boils down to whether relational rules are net liberating or net restrictive. That is where the final battle will be.
- And OO provides something databases currently aren't capable of, easy enforcement of massively complex models filled with complex rules. In the future I'm sure the two will be integrated into some new paradigm, but that day hasn't arrived, and until it does, we'll build models and handle the relationships with them because that's where we need to handles the to apply the rules to them. We still use sql on the backend, after we've verified the potential relationship passes all the rules encoded in the model. SQL need not be hand written, it's quite easy to generate from the model, thus we get the best of both worlds for now. Someday there will be something better, but currently there isn't, at least not in standard usage.
- I have yet to see a decent failure (or messy) scenario for a RDBMS approach compared to OO. Until I see the messy code with my own eyes (and it can't be fixed), I won't believe you. Again, existing DB products do have some flaws, but they are probably not the fault of the paradigm but of bad vendor thinking or poor schema design. Nothing is the winner of all races. Even with the flaws, there is a still net advantage IMO. (I responded to your "total" challenge below. Awaiting another.)
- Great, then your domain falls into those simple categories that don't exceed the limits of databases, good for you. But I'm not trying to convince you, I know that's futile, I'm simply providing the alternative viewpoint for the page.
- Do I smell a DomainPissingMatch? Evidence works better than intimidation. Try it for once. Everyone would love to see an example where databases flop and fall for "large" applications outside of the embedded world. Oracle might find a market they missed. I've seen many biz apps that are far too complex for any one person to grasp the entirety of. Related: OopTeamSize and AreBusinessAppsBoring.
As far as limiting or validating RDBMS-given data by user or other constraints, there are triggers, referential integrity,
AccessControlLists, etc. (See
RaisingExceptionsToTheUser).
If a class wrapper around entity info has most of the typical access primitives (add, change, delete, etc.), which it probably needs to be useful, then one can do just about anything "wrong" to the entity that they could with SQL.
Untrue, since the object model is a model, it would only provide one way to accomplish an appropriate task, preventing you from doing it wrong. If the only way to add an item to an Order is Order.AddItem?(anItem), there's simply no way to do it wrong.
There might be a "proper" method to do a particular task, but there is nothing inherent in OOP that prevents a developer from using the other more primitive methods to do something the wrong way. OOP cannot read minds.
Yes there is... it's the model itself, there is no more primitive method. When you work with a model, you simply can't do things wrong.
- See "Widest Method Access Principle" toward the bottom of GateKeeper.
Further, RDBMS integrity, constraint, and validation rules are enforced regardless of which language or tool uses the information. Its enforcement of rules is more "global" than a language-specific solution. In that sense, it is more "global".
You need to quit thinking of add, remove, update, and delete as database operations, they are not, they are simply operations, and these are the fundamental operations that business rules are tied too. These operations are as common in ram, in models, in other datastructures as they are in a database. We build object models because it is the only way to enforce very complex rules globally on those operations. SQL simply isn't capable as a language to accomplish what a real language can. Until you quit thinking of these as database operations, you'll never get it. Databases don't own those four actions, nor are they somehow being reinvented whenever we do them in memory. Databases are great at managing lot's of data, but they aren't good at managing lot's of business rules.
It is not a matter of "owning" the so called DatabaseVerbs, but bringing consistency and discipline to them. Each class can reinvent them in a different way. If there is a huge expressiveness tax to relational rules, as you imply, I have yet to see a solid example of the limits in code. Until I see failures, I see no reason to abandon the consistency card. All else being equal, go with consistency. That is logical.
Sure, but as I said, all else isn't equal. The database doesn't let me add complex business rules to those verbs, code does. That's the failure, that's why code wins. Code let's me "force" rules on those verbs that can't be bypassed within the model, thus if you only use the model, you can't do anything wrong.
Nobody said databases are to handle every action. It is a matter of proper DivideAndConquer, something that OO seems to ignore, mixing nouns and verbs without a discipline (OoLacksConsistencyDiscussion).
I would also like to see a nice demonstration of the model reigning in improper behavior. That is not a common pro-OO claim. Perhaps we can squeaze out a nice new HolyWar battle over such a claim.
Databases are more "global" than simple RAM. If a program, no matter what paradigm it was written in, allocates a block of RAM for its own immediate use that is less "global" than creating a record in a database. It isn't practical to ignore RAM and keep all state in a database. -- EricHodges
So you re-enter all your customer information if somebody trips over the power cord? Note that there are potentially temporary tables and local "result sets" for task-specific usage. See also ProgrammingWithoutRamDiskDichotomy.
He said that it isn't practical to ignore RAM and keep all state in a database, not that it is practical to ignore disk and keep all state in RAM. -- JonathanTang
- I did not claim that. But generally stuff that is too small or temporary to keep in tables (local or shared) is also too small to bother putting in classes.
I can't keep all state in a database, and I don't think you can or do either. If you have some non-toy code that stores all of its state in a database and none of it in RAM, I'd love to see it. --
EricHodges
Actually, many web-based apps are almost like this, out of necessity. I've got one that uses RAM basically for TemplatingInPhp and string manipulation, and practically everything else is in the database.
Not to say that I like doing this. It makes some things - like storing user input before they've completed a form - ridiculously difficult. But PHP is stateless, so there's not much else I can do. -- JonathanTang
- More generally, http is stateless (i.e. the difficulty certainly isn't limited to PHP, it arises with many technologies for handling web pages)
- Java, Lisp, and .NET have figured out ways around HTTP's statelessness though. They let you program as if you had state, and then take care of marshalling it back and forth between the browser and the server. PHP's capabilities in this area are pretty limited. It has session libraries, but often times these just store the "state" in the database and reference it via cookie that's passed back and forth.
- This is one of those things where DoTheSimplestThingThatCouldPossiblyWork is a really bad idea. Statelessness makes everything hard, so it actually makes sense to go with a more powerful technology than one initially needs; it's guaranteed to avoid future problems.
- Bad idea or not, we're stuck with it for the foreseeable future. The better technologies don't necessarily win out in the marketplace. But this is old news.
- No, no, no. I'm not faulting http. There's actually some advantages to it being stateless. No, I mean one should use powerful technologies with http right off the bat, not simple ones. As you said above with "Java, Lisp, and .NET have figured out ways around HTTP's statelessness though. They let you program as if you had state, and then take care of..." If you don't use such a thing, then you'll reinvent it a little piece at a time, painfully, and badly.
- Let's not confuse stateless transactions with lack of state. State exists within each request HTTP request, it just doesn't persist to the next one. Top is advocating, or seems to be advocating, moving all state to a database.
- What, rather than using HTTP?? Either I'm missing something, or perhaps you're reading more into it than you should...even top wouldn't say that.
- That's how I read what Top is saying. Read his response at the top of this section. Perhaps he will clarify.
- I don't understand the top part of the page in general, and it's hard to see what the page title might be driving at. I rather thought "global" was a pretty well-defined word.
- Basic gist: people often criticize RDBMSs for being a giant collection of global data, with no encapsulation, access control, or business rules. Top is pointing out that classes, barring module/package systems, are also global and accessible from anywhere. He equates classes with data in the database, and points out that there are no more guarantees of integrity for a class than for a relation. (This IMHO is a false equivalence - the correct one is object=data, not class=data - but I shouldn't inject commentary when translating. ;)) And this whole thread down here is a tangent that's basically unrelated to the thesis of the page. -- jt, TOP-translator extraordinaire
- I would be interested to see more details on your equivalency suggestion. Both classes and DB's are interfaces to state, regardless how they are classified. DB's attempt to standardize and factor together state-oriented interfaces, while OO classes leave you on your own. -- top
- (Thanks for the translation.) Oh. Actually that interpretation had in fact crossed my mind, but I rejected it because it was clearly wrong. I guess I shouldn't assume so much.
- One needn't make any parallels at all. A database table is an object, which, as it happens, contains an array (or a set or bag) of objects. All else follows.
- But there are alternative EverythingIsa views of just about anything you can name. -t
It doesn't keep any state in RAM? It reads a byte from the network and writes it to a database? It doesn't calculate anything, decide anything, etc.? -- EH
I did say "uses RAM basically for TemplatingInPhp and string manipulation". The template library is - guess what - an object. But it's a black box as far as my code is concerned, and about the only local variables I need to declare are templates. -- jt
Local variables are state. -- EH
PageAnchor: interface_relativism
Tables are a giant collection of global data. A table can't be compared to an object because an object can carry their rules with them, objects are behavioral entities. Tables can too in the form of triggers, the difference of course is in the expressibility of the language. An object allows full encapsulation and protection of it's data, a table doesn't.
Can you identify a specific failure scenario? I will agree that in some RDBMS, triggers etc. have limits under conditions. But, even with such vendor-specific limits, the multi-language scope of such rules offsets their short-comings. Besides, like it says below, you cannot force a developer to use the "proper" method. RDBMS tend to take a different approach and enforce rules about data instead of (trying to) regulate by limiting the methods available.
It's not about security, it's about encapsulation and enforcement of business rules. That's why a table is a big global variable while and object isn't. It's quite easy with an object model to ensure invalid items can't be placed into an order no matter how complex the rules, the same cannot be said for and order table and item table unless you try and encode all the business rules into triggers, which is damn near impossible because SQL doesn't do business rules anywhere near as well as a real language.
Again, I would like to see a specific scenario. Note that some of this perhaps belongs under GateKeeper, which is more about protection than globalness.
Again I'll say... anOrder.addItem(anItem), it's that simple, in an object model there could be hundreds of complex rules hidden behind that simple call, calculation of taxes, discounts, all of which may have their own complex rules about when they are valid or when they expire etc. An object model puts a structure into place that makes this all simple to enforce, and impossible to forget. Databases can't do that. "Insert (arg, arg, arg) values(arg, arg, arg) into OrterItems?" simply can't be compared, nor can it hide the complexity of the business rules like the object model can.
SQL forces one to keep all business rules in mind each time you write a statement, view some data, change some data, delete some data. Object models don't, they can simply be used, they won't allow you to do anything wrong, if I view anOrder.Total in a model, I know it's correct, I don't have to worry, but if I "select sum(item.price) from item where orderid = 3", I have no such guarantee, in fact, it probably should be "select sum(item.price * quantity) from item where orderid = 3", of course, it's probably more complex than this, and this is exactly what happens when developers use SQL, suddenly your developer is spending a week trying to figure out why the reports don't match the order screen. Models hide complexity and make programming easier, anOrder.Total is far easier than any other alternative.
Because of the "Widest Method Access Principle" (see toward the bottom of GateKeeper), one can probably find a way to total it "wrong" using an Invoice class or invoiceItem class. Further, in practice different groups may want different totals. For example, maybe sales compensation does not depend on sales tax, and so the sales department wants a sum without taxes in it. You seem to be against globals, yet you are proposing that there is One Right Global Interface, such as one and only one right way to total. Again, I belong to the EverythingIsRelative school of thought.
As an example, I used to strive for the concept that every record should have a generic "description" attribute of some kind. (A polymorphic "toText" method in a way.) However, sometimes people/situations wanted long descriptions, other times short descriptions, other times translated descriptions that were more customer-friendly than the internal people needed, etc. Eventually I realized it was easier to just "calculate" them as needed (per task) rather than strive for the One Right Global Generic Description dream. No single description will ever make every stake-holder happy. Einstein was right. Just breed Einstein and Dr. Codd together, and the result will understand what I am trying to say.
Nope, now you got anOrder.Total and anOrder.TaxLessTotal?, both using the exact same code, with Total simply summing TaxLessTotal? plus taxes. You still fail to see the simplicity of an object model. They allow code reuse in ways sql can't, because sql doesn't provide a real language to work with. There is only one right way to total, there may however be different totals the domain is interested in. Let's put it this way... one first builds an object model representing the domain... this model is now the language we use to solve problems. As a language, the object model works better than sql to solve problems in the domain. The object model language is custom tailored to the problem at hand, sql isn't. Until you start seeing an object model as a language, you just won't get it. SQL is a mini language to represent data manipulation, and it works great at that, and an object model is a language to solve the exact problem at hand and it's great at that, better than SQL, since it's tailored to the problem.
When you say, "You still fail to see the simplicity of an object model", you are right. I wish to see some specific code. I already know you think SQL+procedural is the devil's ass, but I want to see for myself. (Note that I don't think SQL is best possible relational language, but that is another topic.) Further, I see little purpose in clutter up your class with customTotalJustForX methods all over the place. Unless they solve OnceAndOnlyOnce, keep them task-specific IMO. Further, does totalOrdersForCountryX belong under an Order object, or a country object? You will have jealous noun fights. See PrimaryNoun. The relationship between nouns and verbs in the real world is many-to-many. OO ignores this fact of life. And, SQL versus code is perhaps a side issue, but still related. I am suggesting calculating totals where they are needed instead of up in some centralized place. If there is duplication, then create a function to share. We don't need SQL to create a function.
I see no jealous noun fight, it's the orders data, so the order get's the method and country.. etc become arguments. Polymorphism let's us use the same message for any number of input objects... I think most of the problems you seem to find with OO, are in your head, or lack of experience building OO based solutions, which we know you never do.
- anOrder.GetTotalFor?(aCountry)
- anOrder.GetTotalFor?(aState)
- anOrder.GetTotalFor?(aCity)
let's compare languages...
- anOrder.Total vs Select sum(total * quantity) as total where orderid = 5
- aCustomer.Name vs Select name from Customer where customerid = 5
- anItem.Total vs Select sum(total * quantity) as total where itemid = 5
- anOrder.AddItem?(anItem) vs Insert into item (field, field, field, field, field, field, field) values (value, value, value, value, value, value, value)
Basically it comes down wanting to work with a language that's custom tailored to the problem at hand. We view the model as a language, you may like select, update, insert, delete for every problem, it may seem simpler to you, it may seem more standard to you, that's fine. But OO'ers like to build languages, then solve the problem in the language, given a schema as input, most of us don't even build the model by hand, it's generated, thus it's not extra work. We also find working with the model far more fun than working with sql, because it requires less work from us, and is easier to reason about because we personify the entities, and find it becomes easier to remember things about them. If your brain doesn't work like this, fine, use sql. But it's been years man, why are you still harping on OO, just let it be, it's clear you don't want to understand OO, you just want to criticize it, and frankly I just don't understand where the motivation for that comes from. OO isn't going anywhere, it is the dominant paradigm in the industry, if anything replaces it (doubtful), it'd be functional, or a nice mix of functional and OO, but c'mon man, you aren't going to bring down OO. Give it up! You seem to have an abundance of energy, why not put it to something more useful like promoting your own style rather than knocking other styles?
- AnonymousDonor
As far as the popularity of OO, see MainstreamOopUsage.
You imply that using SQL results in more total code. It does not. Your list of examples leave out a lot of intermediate steps, such as finding which object to instantiate. Thus, as a comparision tool for code size and effort, I find your examples misleading. And they assume a predictable pattern of variation-on-theme, which is often unrealistic. As far as building a SubLanguage, I do it all the time. It just tends to be local to need rather than application-wide, although I can do that too if needed. SQL is already a sub-language of sorts that provides a standard way to provide CollectionOrientedVerbs. Much of your examples are just collection handling hard-wired into names and not really domain-specific after all. You are just not seeing it for what it really is IMO. You are losing an opportunity for reuse and consistency by mixing domain-specific issues with collection-handling issues.
- [Here's a complete example of calculating order totals in an OO fashion, using ActiveRecord:]
class Order < ActiveRecord::Base
has_many :items
def total
items.map {|i| i.price * i.quantity}.reduce :+
end
end
# then you'd retrieve the total for orderid 3 using:
Order.find(3).total
# equivalent to the SQL query from above:
select sum(item.price * quantity) from item where orderid = 3
- [Including the "end"s, that's six lines of setup; considering the relative length of the resulting Ruby code compared to the corresponding SQL, you'd only need to require that total calculated about four times throughout your codebase to amortise the original cost of setup. And that's without considering that you can never get the Ruby version wrong, in the sense that you can't at one point forget to multiply the prices by quantities, or otherwise mistakenly miscode the business rule required in your query. -DavidMcLean?]
- I'm not sure exactly what you are comparing. But I see 3 problems here. One is that the client won't have the powerful filtering, indexing, and ACID engine that a database typically has, resulting in one, for example, doing sequential filtering instead of using indexes. (This example doesn't have a lot of joins or by-category summing, but often queries do.) And second, you could end up transferring the entire table to the client to filter and sum, whereas doing it at the server means you only send the answer, not the entire set of input data (the answer is usually smaller than the input data). Third, one has to learn a different query-like sub-language for each client app language. JavaScript will have one variation, Dot Net another, Java yet another, etc. With SQL you only have to learn and remember one query language and use the same one on the 25 or so different app languages one typically ends up using over the years. (Further, if you decide to use StoredProcedures for speed or security down the road, you only have to move the SQL, not re-write queries from an app style to SQL.) Anyhow, this is probably getting off the topic of scope and maybe belongs in something like SqlVersusClientQueries?.
- [I'm comparing code size and complexity, specifically for calculating an order total, between an SQL query presented above and the equivalent in Ruby, in response to your claim that the previous examples left out "intermediate code" such as finding the right object (Order.find(3) in the example!). The client as shown here has exactly the same filtering, indexing, and ACID engine that a database has because it's using a database. Your second point, regarding the transfer of a larger table to filter and sum, is half-accurate: The implementation I presented above would require that all of the "items" associated with a given Order be sent across, although not the entire table for either the Order or Item objects involved. Because the total-calculation is defined exactly once, however, in the Order class, it is extremely possible to redefine it once if efficiency proves to be an issue:]
def total
items.sum "price * quantity"
end
- [And every call to total will benefit. As for the variation between languages, there is always variation between languages, no matter what you're doing; the API used to query a database differs between languages whether or not you use SQL. (And SQL is hardly standard between DBMSes anyway!) However, using tools like ActiveRecord makes accessing a database the same as or closer to accessing other things in Ruby, leading to a more generally consistent and idiomatic codebase in which all forms of processing are equally familiar. -DavidMcLean?]
- I'm skeptical that it scales up to complicated or resource-intensive queries. Your example may be a case of ArgumentByLabToy. And Ruby is risky as a team language in my opinion because it makes it tempting to create "clever" JobSecurity idioms. It's kind of a hipper form of the problems described in GreatLispWar. And API's to databases are either relatively simple, or wrappable into simpler API's to hide features not typically used for a given shop. I don't see that as a big problem.
- [Why wouldn't this scale up to "complicated or resource-intensive" queries? Queries are still performed by the database and take the same general form they would otherwise, so a query too "resource-intensive" to process here would not be able to be processed by using "raw" SQL instead. "complicated" queries are for the most part expressable in ActiveRecord directly, especially through application of abstractions it offers such as associations, but as with all sensible ObjectRelationalMapping systems it does permit the inclusion of simple SQL fragments for the unlikely circumstance that it cannot express a given query directly. As for Ruby being poor for teams due to encouraging JobSecurity idioms, that's little more than an assumption on your part and is not generally representative of Ruby projects. As for database API complexity being a "big problem", I don't claim that it is, merely that it isn't consistent between DBMSes and languages; sure, it might be simple (although plenty of database libraries aren't, PHP's standard-library support for MySQL being an example of such), but that doesn't mean it doesn't have to be learnt at all. -DavidMcLean?]
- I'm skeptical the translation between this ruby query language and SQL is fully accurate and reliable. Full SQL is a complex language (for good or bad) and there is a lot of vendor variation. If Ruby or ActiveRecord falls out of style, will the translation libraries be kept up sufficiently? And again, one's SQL knowledge can be used with multiple application languages, ruby won't. Fitting Ruby's own syntax style is not a big enough benefit to overcome the benefits of the sharability of SQL knowledge. Plus, ActiveRecord has competitor query API's. It could be here today, gone tomorrow. You have to be significantly better than the standard (SQL) to unseat it, not just slightly better. Companies find that standards make staffing easier.
- As far as being "less code", one would have to look at an actual application or more realistic example to really compare. There are ways to factor SQL-centric applications to reduce duplication and verbosity, but I'd have to see an actual specimen to recommend a specific course of action, being that there are different techniques for such. Also, sometimes team readability trumps reducing code size. Code size is not the only goal in town.
- This section is off-topic and probably should be spun off to another topic. Suggested titles?
- [TradeoffsOfObjectRelationalMapping? perhaps? ActiveRecord is unlikely to fall out of style or otherwise end up unsupported since it's a major component of RubyOnRails, the biggest Web framework on the platform. Knowledge of SQL itself is in general not transferable between RDBMSes, much as knowledge of Ruby is not necessarily transferable to other application languages; I'll concede that the existence of a standard does in the ideal guarantee some stability, but vendors often don't conform to standards properly no matter how much we'd like them to. As for "team readability", I find it unlikely that "Query.find(3).total" will be any less readable to a team than a single person, especially when compared to "select sum(item.price * quantity) from item where orderid = 3". -DavidMcLean?]
- R-on-R can fall out of style just as quickly as any other framework. And SQL being different per vendor will complicate ActiveRecord query creation also if translating is used. I doubt it magically hides a good many vendor differences, at least not without becoming a complex library with its own quirks. And one can write a wrapper function called something like find_total_for_thing(3). I should have said "general maintenance", not just "reading". Maintenance includes adding and changing queries. Further, OOP is not well-suited for domain modelling in my opinion and that of others. It works fairly well for computational-space components such as GUI's and reports, but not for employees, customers, orders, etc. See OopNotForDomainModeling.
- [RoR could become unpopular and unsupported, but it's nonetheless unlikely, especially considering its current popularity and the impact it's had on Web design. SQL differing per vendor obviously does complicate ActiveRecord's query creation; one of the major strengths of having a library do your query creation is that you aren't exposed to that sort of complexity. And, indeed, you aren't exposed to the complexity of vendor differences when working through an ORM, unless you happen to be developing an adapter to some specific DBMS yourself. As for wrapper functions, yes, you can write those, but do you? With an ORM, you're heavily encouraged to represent the important facts of your system as specific methods in the model; an approach involving direct use of SQL or in general queries tends to be a rather leakier abstraction. Again, I find it unlikely that the implementation in ActiveRecord will be any less maintainable to a team than to a single person. I'd conject that, at least in the field of object-relational mapping, OOP's perceived poor fit for domain modelling may stem from complicated, boilerplate-heavy ORMs such as those for JavaLanguage, and much like everything else in JavaLanguage it gives a poor impression of this object-oriented pattern. Of course, OopNotForDomainModeling is still correct to an extent, as computational modeling is definitely OO's greatest strength, but this doesn't mean object-relational mapping is without value, merely that the bulk of an OO system will be computational models. -DavidMcLean?]
Also, I will indeed wrap *common* SQL idioms behind functions with compact signatures. It is
OnceAndOnlyOnce 101. But I don't believe in having a bunch of little methods called
only once with names like:
getAllPinkScarfsPurchasedSouthOfUtahLastYear(....)
That will create more code, not less, because you are creating a wrapper for stuff that may only be used once. Thus, you incure the cost of both the implementation and the wrapper and reuse won't come into play to justify it.
Plus, I can do things like:
criteria = "taxState not in ('TX','CA','NY')
and invcTotal > 500";
print("Total:", getTotalOrders(criteria));
....
function getTotalOrders(criteria) {
return query(stdDB,
"select sum(amt) from Invoices,
lineItems where invoiceID = invoiceRef and (%1)",
criteria);
}
A simple sub-language AND flexibility together in one. If you can top that, I will be impressed.
Criteria aQuery = new Select(Tables.Invoices)
.Where(
new And(
new Not(Invoices.taxState,'TX','CA','NY')
new Greater(Invoices.invcTotal, 500)));
print("Total:",aQuery.Execute().Inject(0,function(last, each){return last+each.amt;}));
Not that I would ever do such a thing, but you seem to be under the impression that we don't do queries in OO, that we somehow write a method for each task, you are mistaken. Having an OO query API that simply wraps SQL in a type safe manner allowing us to avoid the evil strings you use, is quite common. You still don't get why we don't like SQL, because it's a string. Yes yes I know you like dynamic languages but so I'll let that one go, I like static languages that are flexible and don't like strings because the compiler can't find them when they're broke.
- Note that your example is missing the Join. But, it appears we agree that the type-safety thing is not necessarily about OO. It already has its own debate topics (StaticVsDynamicTyping). Note that the Oracle language PL/SQL is statically typed and is pretty well integrated with SQL. However, databases are supposed to be language-neutral. Perhaps loss of type-safety is a trade-off for such neutrality. And yes, I am a fan of dynamic languages and thus don't care much about type-safety issues. Anyhow, I have yet to see an example of OO being more "natural" or shorter.
It's not missing the join, it doesn't need the join, since it's getting the invoice, and asking it for the total.
- My interface can use line-item info for filtering also. Thus, they are not equiv. I can query both entities with the same function. In the reworked example below, taxState is from the invoice entity and productRef from the line-item entity. And, I am referencing a third entity in order to remove recalled (defective) products from the total. These kinds of multi-entity requests are very common in biz apps. I am not bound by OO's PrimaryNoun rules. I don't want DiscontinuitySpikes when requirements wonder over different entities, because it happens often. Plus, you are reinventing an ugly single-shop query language (ExpressionApiComplaints). Why not stick with a standard? Better an ugly standard than an ugly non-standard.
criteria = "taxState not in ('TX','CA','NY')
and productRef not in (select productRef
from recalledProducts)";
print("Total:", getTotalOrders(criteria)); ....
As far as generating
CrudScreen applications from schemas, I have done similar things using
DataDictionary's. However, it is more flexible to use the declarative model directly rather than turn it into code (
CodeGenerationIsaDesignSmell), although non-OO code generation is still a possibility and I doubt it would be more total code than OO. You may personally prefer OO, but it is not objectively better. It might be "in style", but code even in languages like Java tends to be procedural in practice.
Only to you, to many of us, they are OO in practice.
- See below about the "lip service" comment.
(Moved comments about VB to
ThingsWeHateAboutVbClassic)
A database can basically be viewed as one giant GodClass with instance variables for every property and methods for every operation. Classes don't typically allow queries of internals based on arbitrary expressions, but it's possible to give a class generic methods that allow anyone to create new methods from other kinds of primitives. Collections classes need these. In that context, it behaves as and results in problems similar to a GodClass.
Relational is as much about constraints (rules) as it is about abilities. Sure, one *could* put methods in to make classes resemble RDBMS, but one is relying on the designer and conventions to stick to the rules. I don't see much beyond TuringEquivalency in such a notion.
(Moved some material to FrustrationOverNotGettingOopDiscussion)
Declarative Interface versus Imperative Interface
Aside from the issue of whether access to the table is as "global" as access to a class (above), part of the difference is that databases generally have a "declarative interface" and OOP generally has a behaviorist interface. OO thinking sees behavioral interfaces as superior because one can wrap what they see as "raw" data with behavior so that one can change the underlying source of the information without changing the interface. Technically one can do the same with table views, and even more so with column-views if they were supported more widely, but this is missing the point and probably would not change the landscape much. The data interface approach leaves most of the behavior to relational algebra, which has its own benefits, namely consistency in terms of CollectionOrientedVerbs being a standard. I don't see enough drawbacks to a declarative interface approach to give up the consistency and inter-language share-ability it brings. I am still awaiting some good behavioral interface show-off examples. Relational trusts its operations to be sufficient for most work while OOP thinks that every object needs custom ones.
Generally with a relational-centric design, "actions" are split into standardized CollectionOrientedVerbs (relational algebra), and application-specific tasks. OO does not make a distinction between these, seeing them all as behavior to be tacked on to an entity. CollectionOrientedVerbs perhaps are associated with one and only one PrimaryNoun in some cases, but app-specific tasks are generally not assumed to be associated with any PrimaryNoun. OO sees the association as a good indexing and/or packaging technique, while relational-centric design tends to see it as arbitrary. This is because:
- An operation may involve zero or many entities, not just one.
- The quantity of entities involved changes over time.
- Which entity may be involved in a task is seen as an implementation detail to be "abstracted away" rather than hard-wired into the model.
- Association for documentation purposes can be done with meta information. This allows not only multiple entities to be associated with a given task, but the level of association may also be tracked. (Ideally, subroutines should be stored as database records, not files or blocks within files in my opinion. This way, meta information can more easily be added to the routine schema to serve whatever documenting or indexing is desired.)
--top
[Hmmm. Maybe some of the above can be moved to PrimaryNoun.]
See also: GateKeeper
CategoryScope, CategoryBusinessDomain, CategoryExample, CategoryOopDiscomfort