Re: Concept-oriented programming



Responding to Savinov...

concept Persistent // Describes logic of persistent storage
reference { ... }
object { ... }

concept Account in Persistent // Describes logic of account management
reference { ... }
object { ... }
concept Customer in Persistent // Describes logic of customer management
reference { ... }
object { ... }


What you are describing here is hierarchical composition using inheritance. The OO paradigm studiously avoids such hierarchical dependencies because static hierarchies are difficult to maintain. But my main problem lies in what is going on inside { ... }.


So why do you think that everything in real life is convenient to represent by hierarchical addresses like <country, city, street, house>, but objects do not deserve this and they have to be represented in flat narrow context by predefined system identifiers? Assume that you need to send a letter to some person with the address like this one: 0x1a2b3c4d5f. Probably you will say that it is inconvenient. But why you think that objects in OOP are an exception and they have to be represented only by such system level identifiers? I do not see any logic here.

How is a compound identifier {country, city, street, house} hierarchical? Compound identifiers just define subset membership. IOW, a Venn Diagram is a flat representation.

Where did I say objects could not have explicit identifiers?

You also say that OO paradigm avoids hierarchies and it is also very strong statement. I would say that in any approach to programming we model some problem domain and the modelling technique has to be adequate to the structure and rule existing the domain. So if we have hierarchies (and we do have them in most problem domains) then the should be represented as hierarchies also in our program. For example, if accounts exist in the context of a bank and has also some sub-accounts then this structure should be easily modelled in our programming language. And now you say that OO paradigm does not encourage hierarchies. If you would say that OOP is not able to manage hierarchies, i.e., it does not support them, then I would completely agree with you (indeed, OOP does not have any construct that supports a hierarchy at run-time). But this does not mean that we do not need them just because what we need is determined by our problem domain. If a problem domain has cyclic structures then our language should support them (and here we go in the direction of domain languages and language-oriented programming). Thus, logically, I am interpreting your claim as follows: hierarchies are so rare in problem domains that a paradigm does not have to support them. Am I right? In contrast, in CoP we assume that hierarchy is the structure of space where objects live in most problem domains and hence it has to be directly supported in a paradigm. Actually, it is one important difference between OOP and CoP.

Where did I say the OO paradigm does not support OO generalization relations? All I said was that one prefers delegation to /complex/ generalizations whenever possible.


If Persistent storage needs to be transactional then we can include it
into the corresponding concept, say called Transactional:

concept Persistent in Transactional // Transactional behaviour
reference { ... }
object { ... }

So what is important here:

- Child concepts like Account and Customer implement only their own
business logic without external things like openDatabase(),
startTransaction() and lockRecord() spread all over their code which
then need to be manually maintained.


Nobody should be doing that in a well-formed OO application. Separation of concerns is fundamental to the OO paradigm so processing unique to particular persistence mechanisms should not be anywhere near the business logic of managing Customers and Accounts. However, ...



As I already demonstrated (and as it is much better demonstrated in other sources) - it is not the question whether it should be done or not. The point is that it cannot be done appropriately in OOP - it is simply not possible because of the nature of OOP. (Again, it is not my personal opinion - it is an established fact.) Just provide a code that can solve this problem and you see it yourself. For example, you have an account number and a person number stored somewhere. Try to access any property of these two indirectly represented objects. In CoP (and AOP) you can do it in two lines as if these objects were represented by primitive references:

There is no logical relationship between persistence mechanisms and transaction processing in any problem space that I know of. Both can be implemented without the other.

The OO paradigm uses problem space abstraction to ensure that the software structure emulates the problem space infrastructures. That's because such emulation enables long-term stability since customers don't like change any more than software developers so they accommodate change in a manner that will cause minimal disruption to their environment. If the software structure emulates the customer's structure, then those changes should have minimal impact on the software.

As soon as one introduces artificial static structures for the convenience of the developer, one is introducing structures that are not present in the problem space and one risks shotgun refactoring when the requirements change.

account.getSomething();
persons.getSomethingDifferent();

In OOP you will have to write it as follows:

Object account = resolvePlusSecurityPlusPersistenceEtc(accountNumber);
account.getSomething();
Object persons = resolvePlusSecurityPlusPersistenceEtc(personNumber);
persons.getSomethingDifferent();

So in OOP you again and again have to call one and the same code for each individual access. There is no other solution - if you have one then provide an example. So this problem has a little to do with CoP (CoP is able to solve it but it is not its main purpose) - it one of the well known limitations of OOP.

Yet another straw man example of how not to write OO applications that you then criticize. The OO paradigm uses quite different mechanism for dealing with this sort of thing.


- Base concepts play the role of containers, wrappers, interceptors,
scopes, borders, spaces, environments etc.

- Base concepts actively and automatically contribute to the behaviour
provided by child concepts

- If we switch to an OODB then we simply change our base concept:
concept Account in PersistentOODB
concept Customer in PersistentOODB
Notice that concepts Account and Customer need not to be changed (if
developed correctly).


This is one manifestation of the problem I see with CoP. If nothing else the business logic of the account management needs to be defined "in" PersistentOODB rather than "in" PersistentRDB. So if one changes the database mechanism one must perform at least some surgery on the account management logic.


No. If appropriately designed, then persistence can be separated from account management and other business logic issues.

How? Somewhere in the code for the subsystem where Account and Customer objects are needed you are going to have a statement with the "in" clause for Account and Customer that specifically links to PersistentOODB rather than PersistentRDB. If the database is changed, then that line of code has to be changed.

That is because if one separates concerns with subsystems, the subsystem interface prevents /any/ visibility of the subsystem implementation. So in a well-formed OO application the Account and Customer objects would not be touched in any way because they wouldn't know that PersistentRDB or PersistentOODB objects even existed.


Again, this problem cannot be solved in OOP without code duplication because persistence is typial cross-cutting concern. We can call persistent management functions just before an object is accessed or we could put them in the target class itself - any such solution assumes that we are responsible for getting object primitive reference and doing all other work that has nothing to do with account or person management. The goal consists in avoiding such actions and it is not possible in OOP. If you know how then provide an example - and I will try to show why it is wrong.

Sorry but the first sentence is just completely wrong. In fact, as I already pointed out for things like persistence one commonly reuses the access mechanism code across /applications/, not just objects.

However, I suspect it is much worse than that. Things like the construction of queries are dependent on both the persistence paradigm and the specific attributes of Account and Customer. So very specific code has to be provided somewhere in the hierarchy that is tailored to do that within all those { ... }.


Ok, good design and separation of concerns is not a simple issue at all. And it still requires some experience in CoP. Clear that CoP itself will not solve the problems - it provides support but how these mechanisms will be used is already another issue.

Again, my pushback is: what is the benefit? The OO paradigm already provides extensive facilities for separation of concerns because such separation is a cornerstone of maintainability. That's pretty much what encapsulation is all about.

It seems to me that code has to reside within the same scope as Account and Customer. That would mean that any time something changed in one of the other concepts Account and Customer are "in", you would be modifying the scope where the account management logic lives.


Anything that exists in some scope or environment depends on it and relies on its functions in one or another way. We can minimize this dependence but it hardly make sense - it is better to formally specify it. For example, Account and Customer concepts expect some concrete support from their possible parent concepts. Just like employee can expect some concrete environment to be able to work or anything that is going to live within some other element.

And my argument is that such support should not be implemented in a subsystem whose mission is managing accounts and customers at all. If it is in that subsystem -- no matter how well decoupled it is -- one needs to change the subsystem and that risks breaking the logic the subsystem is supposed to be resolving.


How do you express such phenomena as subsystem, abstraction, memory
management in OOP? I think OOP does not provide anything special for
that. For example, hypothetically we might propose to use the following
language constructs:



Subsystems are clearly and unambiguously defined at the OOA/D level. They are typically implemented using two-way Facade patterns to encapsulation them. [There is a description of the standard subsystem Bridge Model in my blog in the category on Application Partitioning.]



Yes, we can use various patterns to implement different types of
functions. But OOP itself does not enforce any concrete logic or
responsibilities. In other words, if we have a class diagram then how
can we figure out what class in it is a sub-system using only OO
specific declarations like inheritance? Actually, we cannot. Moreover,


You are focusing on the OOPLs which do not provide direct support for higher level notions like application partitioning (i.e., Systems Engineering concern). However, in UML one uses an entirely different diagram (Component) to describe subsystems quite unambiguously in OOA/D. OOP then provides standardized mechanisms (e.g., Facade patterns or the Bridge Model) for implementing those subsystems.


Component is quite different from subsystem. Component can be viewed as a block box with some interface. Subsystem assumes some hierarchy (as prefix "sub" suggests). Indeed, if have a subsystem then there has to be a system where it is in. And, further, there could super-system which it contains. Of course, we can say that these notions existed before but the question what is the basis for defining subsystem-supersystem relation? Actually such a basis does not exist except for some information observations. And it is precisely what CO paradigm changes: it says that any object is actually a subsystem. In particular, it exists within some superobject (its parent) and it may have child objects. It is not informal role because they have formally defined responsibilities. As a consequence, if we declare such a relationship then objects will behave in a predefined way. This is why I said that subsystems are not supported in OOP (and even in OOAD) - but they can be manually implemented just as in other approaches to programming or system design if the modeller knows what he is doing (the paradigm cannot help him in the sense).

I suggest you take a look at the UML definition of the standard <<subsystem>> stereotype of Component. I'm sorry but what you are saying here is just wrong.


It seems to me that as soon as you are making choices and introducing stacks you are introducing overhead compared to direct peer-to-peer collaboration.



No doubt, that all other things equal, CoP will be slower that OOP
accessing objects. Just like virtual functions in OOP are slower than
normal functions because such calls are intrinsically indirect. However,
the overall performance of a concept-oriented program could be higher
and here is why. What we are doing in OOP when we need to access an
object? We are making many auxiliary actions so a typical access looks
as follows:

// Example OOP
loadAccount(accNumber);
Object o = resolveAccountNumber(accNumber);
lockAccount(o);
checkPermissions(o);

o.getBalance(); // line 1: Direct call using primitive reference

unlockAccount(o);
unloadAccount(accNumber);

Here is what we are doing in CoP:

// Example CoP
account.getBalance(); // line 2: Indirect call using custom reference


You neglect to show how you get the right "account" object. You are also going to need statements equivalent to the first two in the OOP example to do that. (Unless you always pass the "account" reference as an argument to the method, which is a serious no-no for maintainability.)


Yes, we manipulate custom references like account or person numbers or remote reference or whatever a programmer/domain_expert needs. And having such indirect identifiers is one of the main design goals of CoP. It is more convenient for the same reason as computer names are more convenient than IP addresses. And, as I demonstrated, the overall performance is quite comparable in most cases while the code is much more clear and maintainable. (Again, in both approaches (OOP and CoP) we are executing the same operations but differently described in the source code.)

But you still left off how the application solution gets the right account instance. That is quite misleading when trying to compare to the "OOP" example when making a point about keystrokes.


The locking is orthogonal to the reason one wants an account balance. It is either because there are explicit business policies around database access (e.g., two phase commit) or because there are concurrency issues. Either way there are better ways to deal with that and such code would never appear in the same method that was invoking getBalance.


May be. But then there will another method which makes the lock. It is precisely the problem with cross-cutting concerns I mentioned several times which cannot be avoided in OOP. We cannot *enforce* the logic of object locks (or whatever other common logic we need for several classes of objects) in a declarative manner. The only OO solution here consists in making these locks manually (normally, as you noticed using some other functions) whenever we need them, for example, whenever an object is about to be accessed. In contrast, in CoP we declare that some classes are *in* the context of some logic, say, object locking strategy, and after that we can forget about this issue - it will be guaranteed that our objects are locked and unlocked correctly.

You only forget about it where getBalance is invoked. Somebody still has to write that code explicitly in your CoP object just as it would be done in an OO resolution of concurrent processing or two-phase commit. In a well-formed OO application that would be managed in one place, just like a CoP object; it just wouldn't be in the method that needs an account balance.

But this underscores a point I made in an earlier post: I don't think it is a good idea to hide the stuff you are hiding. One may separate concerns but one doesn't want them to be invisible in the context where the are important to the solution. While I wouldn't deal with them as in your example, I would certainly deal with them somewhere in the context of accessing an account balance.

For example, if business requirements demanded a two phase commit protocol, that places constraints on when I can access the account balance and what I can do with it. In that case I would want to see how those constraints affect sequencing of operations in my solution. So I would enforce them with object state machine transitions. Similarly, if there were nonfunctional requirements that were resolved with concurrent threads, I would want that thread management to be visible relative to sequencing of the rest of the problem solution. IOW, I want anything important to the correctness of my access of an account balance to be visible _at the same level of abstraction_.


I am not sure that I understand you. Roughly speaking, abstraction means hiding details. And hence we do not want to see what belongs to another abstraction level. If account management and transactionality are different absraction levels then they should be hidden rather than visible to each other. So it is design issue. The main point is that the programming approach has to provide mechanisms that allow us to separate abstraction levels in a principled manner. CoP is able to do this and with OOP I am not sure (at least cross-cutting concerns cannot be separated in OOP).

My point is that one has to be selective about what details one hides. I have worked with OR applications where an entire linear programming algorithm was encapsulated in a single method call. That's because the linear programming algorithm is defined outside the problem context and is not going to change when the problem requirements change. So those details are not relevant to the problem in hand and are hidden even though there might be 100 KLOC of code in the algorithm package.

OTOH, two-phase commit or performance requirements are very specific to the problem in hand because they constrain flow of control in the specific problem solution. So I want to see those business rules in the solution at the same level of abstraction that I want to see how the business rules for processing withdrawals are resolved.

For example, when dealing with two-phase commit, I want to be able to look at a Statechart and see what the sequencing constraints are for the commit AND what behaviors are involved in processing a withdrawal transaction at the same level of abstraction.


What I am pushing back on is getting the right reference to pass. A Customer has two Accounts at a bank for saving and checking. The Customer needs the balance of the checking Account. How does it navigate to that particular Account object to get its reference?

There are only three ways to do that:

(1) Provide a Find(accountNumber) behavior as an Account class method and have it search all Account objects for a match and return the reference to the right one. That's the RDB solution and it is usually highly inefficient compared to (2).

(2) Implement a relationship using a pointer reference to the checking account that Customer navigates whenever it needs a checking account balance. This is the preferred OO technique.

(3) Pass the checking account reference to the method needing the balance as an invocation argument. From an OOA/D perspective, this is the worst way to do it because it immediately creates a hierarchical dependency because the caller needs to know exactly which Account the method needs.

Your paragraph makes me suspect that you would expect that in CoP-based construction one would /always/ use (3). IOW, CoP employs the lambda view of functional programming. That's fine, but it is definitely contrary to good OO practice and would place CoP far outside the OO context.


I dot see any problem at all. Just as OOP, CoP does not care where and how references are obtained. In this context, the only thing CoP adds is that reference may have custom format. So we can create instances and get new references (in custom format). Then we can pass them or store them or do whatever we want. If we do not have a right reference then CoP cannot help us here (obviously) just as OOP cannot help us if we do not have a primitive reference. The programmer has to implement the corresponding mechanisms for searching references and how concretely it is done is already a design issue. Concrete algorithm can very different. (Or I did not understand your question.)

Oh, wow! Clearly we are on different planets here. I am not going to go into a long discussion of why it is so, but passing object references to methods (other than constructors or referential attribute setters) is a very bad OOA/D practice because it creates exactly the sort of hierarchical dependencies that gave Spaghetti Code a bad name.


OTOH, if CoP is not meant to be OO, that's fine; maybe CoP will take over the software space. But it isn't an OO paradigm if solutions are constructed as you propose.



In fact, the only thing I have claimed (and one of the design goals) is
that CoP is backward compatible with the OOP main principles, i.e.,
under certain simplifying conditions we will get precisely what is
expected by an OO programmer. In its full featured form, CoP is
definitely significantly different from OOP.


Then I am confused by why you opened this discussion on an OO forum.


Precisely for that reason: CoP can be qualified as an extension or generalization of OOP. So this forum is the closest one to CoP.

I think you need your own forum. B-) As you have described it and because of comments you have made like that immediately above, I think the CoP approach is incompatible with OO development in the same sense that P/R or functional programming are incompatible. IOW, one can't mix and match across disparate paradigms.

CoP may replace OO development someday when it become evident it provides a maintainability advantage, but until then they are different ball games.

However, to make that assertion stick I think you need to address how you achieve the same maintainability that one can achieve with OO since maintainability is the primary goal of the OO paradigm. I have identified several potential concerns for maintainability but none of your responses give me a warm ands fuzzy feeling that those issues are resolved in the CoP approach.


Maintainability in OOP has serious problems (because of code duplication caused by cross-cutting concerns) so it is precisely the problem that CoP wants to overcome rather than to reach :) However, at the same time, CoP wants to retain all the numerous advantages and the whole spirit of OOP.

Where is that code duplication? I don't see it in any of the applications I do -- other than computing space infrastructure like collection classes for * relationships that one does not deal with when creating an application to solve a specific problem because they are already in libraries.

So far all you have provided are straw man examples of "OOP" that are just bad examples of how to do OOP.


*************
There is nothing wrong with me that could
not be cured by a capful of Drano.

H. S. Lahman
hsl@xxxxxxxxxxxxxxxxx
Pathfinder Solutions
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
"Model-Based Translation: The Next Step in Agile Development". Email
info@xxxxxxxxxxxxxxxxx for your copy.
Pathfinder is hiring: http://www.pathfindermda.com/about_us/careers_pos3.php.
(888)OOA-PATH



.



Relevant Pages

  • Re: Concept-oriented programming
    ... concepts into it (inherit from it, in OOP terms) like concepts Account ... reference {... ... So very specific code has to be provided somewhere in the hierarchy that is tailored to do that within all those {... ...
    (comp.object)
  • Re: Concept-oriented programming
    ... concepts into it like concepts Account ... reference {... ... But why you think that objects in OOP are an exception and they have to be represented only by such system level identifiers? ... in CoP we assume that hierarchy is the structure of space where objects live in most problem domains and hence it has to be directly supported in a paradigm. ...
    (comp.object)
  • Re: Bad Code (that works) help me re-write!
    ... oop. ... posted with wrong account, sorry for attatched disclamers in other ... posts. ...
    (comp.lang.python)
  • Re: Concept-oriented programming
    ... The standard OOA/D approach is to encapsulate the database access mechanisms in a separate subsystem that understands the particular storage paradigm. ... When an Account object needs to be created in the account management subsystem a factory object will invoke getAccountData and instantiate the object with the returned data. ... As I understand CoP, one would create an Account reference with the various getBalance and whatnot accessors that the account management subsystem would use. ...
    (comp.object)
  • Re: Administrator account disabled in SBS 2008 but password is now kn
    ... demonstrating to a very high value customer. ... passwords for both my user account and the Administrator account. ... The Server service is disabled. ... extension of a trial installation which expired due to non-use when there ...
    (microsoft.public.windows.server.sbs)