A little intro:
Class contains fields and methods (let me skip properties this time).
Fields represent a state of the class.
Methods describe behavior of the class.
In a well-designed class, a method won't change the class's state if it throws an exception, right? (In other words, whatever happens, class's state shouldn't be corrupted)
Question:
Is there a framework, a design pattern, best practice or a programming language to call a sequence of methods in a transactional style, so that either class's state don't get changed (in case of exception), or everything succeeds?
E.g.:
// the class foo is now in the state S1
foo.MoveToState2();
// it is now (supposed to be) in the state S2
foo.MoveToFinalState();
// it is now (supposed to be) in the state, namely, S3
Surely, an exception might occur both in MoveToState2() and MoveToFinalState(). But from this block of code I want the class foo to be either in the state S1 or S3.
This is a simple scenario with a single class involved, no if's, no while's, no side effects, but I hope the idea is clear.
Take a look at the Memento pattern
The memento pattern is a software design pattern that provides the ability to restore an object to its previous state (undo via rollback).
Not the most efficient method, but you could have an object that represents your transactional data. When you start a transaction, make a copy of the data and perform all operations on that. When the transaction ends successfully, move the copy to your real data - this can be done using pointers, so need not be too inefficient.
Functional programming is a paradigm that seems to fit well to transactional computations. Since no side-effects are allowed without explicit declaration, you have full control of all data flow.
Therefore software transactional memory can be expressed easily in functional terms - See STM for F#
The key idea is the concept of monads. A monad can be used to model an arbitrary computation through two primitives: Return to return a value and Bind to sequence two computations. Using these two, you can model a transactional monad that controls and saves all state in form of continuations.
One could try to model these in an object-oriented way through a State+Memento pattern, but generally, transactions in imperative languages (like the common OO-ones) are much more difficult to implement since you can perform arbitrary side-effects. But of course you can think of an object defining a transaction scope, that saves, validates and restores data as needed, given they expose a suitable interface for this (the patterns I mentioned above).
The simplest and most reliable "pattern" to use here is an immutable data structure.
Instead of writing:
foo.MoveToState2();
foo.MoveToFinalState();
You write:
MyFoo foo2 = foo.MoveToState2();
MyFoo finalFoo = foo2.MoveToFinalState();
And implement the methods accordingly - that is, MoveToState2 does not actually change anything about MyFoo, it creates a new MyFoo that is in state 2. Similarly with the final state.
This is how the string classes in most OO languages work. Many OO languages are also starting to implement (or have already implemented) immutable collections. Once you have the building blocks, it's fairly straightforward to create an entire immutable "entity".
This would be pretty ugly to implement everywhere, but just saving the state locally, then restoring it in the case of an exception would work in simple scenarios. You'd have to catch and rethrow the exception, which may lose some context in some languages. It might be better to wrap it if possible to retain the context.
try {
save state in local variables
move to new state
} catch (innerException) {
restore state from local variables
throw new exception( innerException )
}
When using object copy approach, you have to watch out that the statements to be rolled-back are only affecting the object's or data itself (and aggregates).
But things are getting really difficult if the side-effects of the statements are "more external". For example I/O operations, network calls. You always have to analyze the overall state-changes of your statements.
It gets also really tricky if you touch static data (or evil mutable singletons). Reverting this data isolated is difficult, because other threads could have modified them in between (you could face lost updates).
Reverting/rollback to the past is often not so trivial ;)
I would also consider the saga pattern, you could pass a copy of the objects current state into MoveToState2 and if it throws an exception you could catch that internally and use the copy of the original state to rollback. You would have to do the same with MoveToState3 too. If however the server crashed during a rollback you might still get corrupted state, that's why databases are so good.
Transactional memory fits here the best.
An option could be a transactional storage. Sample implementation you can find here:
http://www.codeproject.com/KB/dotnet/Transactional_Repository.aspx
Memento pattern
Also let me describe a possible pattern on how to implement such behavior:
Define a base class TransactionalEntity. This class contains dictionary of properties.
All your transactional classes inherit from the TransactionalEntity and should operate over some sort of Dependency Properties/Fields, i.e. properties(getters/setters) which store it's values not in local class fields, but in dictionary, which is stored in the base class.
Then you define TransactionContext class. TransactionContext class internally contains a set of dictionaries (one dictionary for each entity that participates in the transaction) and when a transactional entity participates in transaction, it writes all data to the dictionary in the transaction context. Then all you need is basically four methods:
TransactionContext.StartTransaction();
TransactionalEntity.JoinTransaction(TransactionContext context); //if your language/framework supports Thread Static fields, then you do not need this method
TransactionContext.CommitTransaction();
TransactionContext.RollbackTransaction();
To sum up, you need to store state in base class TransactionalEntity and during transaction TransactionalEntity will cooperate with TransactionContext.
I hope, I've explained it well enough.
I think a Command Pattern could be well suited to this problem.
Linky.
I was astonished that no one suggested explicitly the simplest pattern to use .. the State Pattern
In this way you can also eliminate that 'finalState' method and just use 'handle()'.
How do you know which final state is?
The memento pattern is best used with the Command pattern, and usually applies to GUI operations to implement the undo/redo feature.
Fields represent a state of the class
Fields represents the state of the instanced object. You use many times wrong definitions of the OOP terms. Review and correct.
Related
In oop we seek to encapsulation. We try not to expose internal state via getters or by public fields, only expose methods.
So far so good.
In situation when we would like to operate on multiple Entities we introduce Service.
But how this service can operate freely on these entities?
If all (both Service and Entities) were in the same package, Entities could expose package private methods or fields and Service could use them, preserving encapsulation. But what when Entities and Service are from different packages? It seems that Entities should either expose public getters (first step to anemic model and leackage of logic from Entities), or public methods executing logic that is specific to the needs of service, possibly introduced only by requirements of this service - also seems bad. How to tackle this?
In the context of OO, the most important thing for you to understand is that objects respond to messages, and that in OOP in particular, methods are how these responses are implemented.
For example, imagine you have a Person object to which you (as the programmer) have assigned the responsibility to respond to the "grow" message. Generally, you would implement that as a Person.grow() method, like this.
class Person {
int age;
public void grow() { this.age++; }
}
This seems fairly obvious, but you must note that from the message sender's perspective, how Person object reacts is meaningless. For all it cares, the method Person.grow() could be triggering a missile launch, and it would not matter because some other object (or objects) could be responding in the right way (for example, a UI component updating itself on the screen). However, you decided that when the Person object handles the "grow" message, it must increment the value of its age attribute. This is encapsulation.
So, to address your concern, "public methods executing logic that is specific to the needs of service, possibly introduced only by requirements of this service - also seems bad", it is not bad at all because you are designing the entities to respond to messages from the services in specific ways to match the requirements of your application. The important thing to bear in mind is that the services do not dictate how the entities behave, but rather the entities respond in their own way to requests from the services.
Finally, you might be asking yourself: how do entities know that they need to respond to certain messages? This is easy to answer: YOU decide how to link messages to responses. In other words, you think about the requirements of your application (what "messages" will be sent by various objects) and how they will be satisfied (how and which objects will respond to messages).
In situation when we would like to operate on multiple Entities we introduce Service.
No we don't. Well, I guess some people do, but the point is they shouldn't.
In object-orientation, we model a particular problem domain. We don't (again, shouldn't) discriminate based on what amount of other objects a single object operates. If I have to model an Appointment and a collection of Appointment I don't introduce an AppointmentService, I introduce a Schedule or Timetable, or whatever is appropriate for the domain.
The distinction of Entity and Service is not domain-conform. It is purely technical and most often a regression into procedural thinking, where an Entity is data and the Service is a procedure to act on it.
DDD as is practiced today is not based on OOP, it just uses object syntax. One clear indication is that in most projects entities are directly persisted, even contain database ids or database-related annotations.
So either do OOP or do DDD, you can't really do both. Here is a talk of mine (talk is german but slides are in english) about OO and DDD.
I don't see the usage of getters as a step towards an anaemic model. Or at least, as everything in programming, it depends.
Downside of anaemic model is that every component accessing the object can mutate it without any enforcing of its invariants (opening to possible inconsistency in data), it can be done easily using the setter methods.
(I will use the terms command and query to indicate methods that modify the state of the objects and methods that just return data without changing anything)
The point of having an aggregate/entity is to enforce the object invariants, so it exposes "command" methods that don't reflect the internal structure of the object, but instead are "domain oriented" (using the "ubiquitous language" for their naming), exposing its "domain behavior" (an avoidance of get/set naming is suggested because they are standard naming for representing the object internal structure).
This is for what concern the set methods, what about get?
As set methods can be seen as "command" of the aggregate, you can see the getters as "query" methods used to ask data to the aggregate. Asking data to an aggregate is totally fine, if this doesn't break the responsability of the aggregate of enforcing invariants. This means that you should watch out to what the query method returns.
If the query method result is a value object, so, immutable, it is totally fine to have it. In this way who query the aggregate has in return something that can be only read.
So you could have query methods doing calculation using the object internal state (eg. A method int missingStudents() that calculate the number of missing student for a Lesson entity that has the totalNumber of students and a List<StudentId> in its internal state), or simple methods like List<StudentId> presentStudent() that just returns the list in its internal state, but what change from a List<StudentId> getStudents() its just the name).
So if the get method return something that is immutable who use it can't break the invariants of the aggregate.
If the method returns a mutable object that is part of the aggregate state, whoever access the object can query for that object and now can mutate something that stays inside the aggregate without passing for the right command methods, skipping invariants check (unless it is something wanted and managed).
Other possibility is that the object is created on the fly during the query and is not part of the aggregate state, so if someone access it, also if it is mutable, the aggregate is safe.
In the end, get and set methods are seen as an ugly thing if you are a ddd extremist, but sometimes they can also be useful being a standard naming convention and some libraries work on this naming convention, so I don't see them bad, if they don't break the aggregate/entity responsibilities.
As last thing, when you say In situation when we would like to operate on multiple Entities we introduce Service., this is true, but also a service should operate (mutate, save) on a single aggregate, but this is another topic 😊.
I'm having trouble wrapping my head around state-based functionality for an invoicing system we are currently building. The system will support calculation, manual approval, printing, and archiving of invoices.
At first I thought we should use the State Pattern to model this. An invoice would be the context, which delegates printing, archiving, etc. to its currently assigned state.
But this is obviously a bad idea, because the different states (created, approved, printed, archived) should not support the same operations. E.g., you shouldn't be able to print an invoice, which hasn't been approved before. Throwing exceptions for unsupported operations would be a violation of LSP. I found a general description of this problem here.
Does anybody have an idea, how to implement this appropriately?
PS: I'm aware that this might sound like some lame-ass homework assignment, but it's not; I need this for a real world system.
You're basically creating a workflow of application states, where at each state the available operations on an invoice change. The state pattern doesn't seem appropriate, but you can still use it if you also create some operations like boolean canPrint() that would have to be used before calling print(). print() would have a contract that allows throwing exceptions if canPrint() returns false. This way, subclasses wouldn't break that contract. Another option is to have a boolean tryPrint(), that will only print if it can, and return whether it printed.
But, if the states support mostly non-overlapping operations, then maybe the state pattern is not the solution. Take a step back and look for better ways, without trying to fit a specific pattern to your problem. One way is to create a separate class with the necessary operations for each "state": like CreatedInvoice, ApprovedInvoice, etc. These classes would only have the operations they support.
Chain of Responsibility Pattern might help you here.
Adding the how part and fixing the link.
There can be Calculator, Approver, Printer and Archiver classes which are handler classes. These can have processRequest() overridden from a parent abstract class. Invoice can be a class which is passed to each handler's processRequest() method. The advantage with using the pattern here is newer handlers can be added dynamically and chain links with sequence of handlers can be changed easily.
Whether the State Pattern is really appropriate to your situation is not certain, but if it's not, Liskov is not the reason. Throwing some sort of "invalid operation in current state" exception can be defined as possible and valid in the state interface, and then subclasses doing this do not violate LSP.
The classic example used for the State Pattern in the GoF Design Patterns book is a TCPConnection, which definitely has operations not supported or sensible in all states. You can't transmit on a closed connection, for example.
By putting functionality into a function, does that alone constitute an example of encapsulation or do you need to use objects to have encapsulation?
I'm trying to understand the concept of encapsulation. What I thought was if I go from something like this:
n = n + 1
which is executed out in the wild as part of a big body of code and then I take that, and put it in a function such as this one, then I have encapsulated that addition logic in a method:
addOne(n)
n = n + 1
return n
Or is it more the case that it is only encapsulation if I am hiding the details of addOne from the outside world - like if it is an object method and I use an access modifier of private/protected?
I will be the first to disagree with what seems to be the answer trend. Yes, a function encapsulates some amount of implementation. You don't need an object (which I think you use to mean a class).
See Meyers too.
Perhaps you are confusing abstraction with encapsulation, which is understood in the broader context of object orientation.
Encapsulation properly includes all three of the following:
Abstraction
Implementation Hiding
Division of Responsibility
Abstraction is only one component of encapsulation. In your example you have abstracted the adding functionality from the main body of code in which it once resided. You do this by identifying some commonality in the code - recognizing a concept (addition) over a specific case (adding the number one to the variable n). Because of this ability, abstraction makes an encapsulated component - a method or an object - reusable.
Equally important to the notion of encapsulation is the idea of implementation hiding. This is why encapsulation is discussed in the arena of object orientation. Implementation hiding protects an object from its users and vice versa. In OO, you do this by presenting an interface of public methods to the users of your object, while the implementation of the object takes place inside private methods.
This serves two benefits. First, by limiting access to your object, you avoid a situation where users of the object can leave the object in an invalid state. Second, from the user's perspective, when they use your object they are only loosely coupled to it - if you change your implementation later on, they are not impacted.
Finally, division of responsility - in the broader context of an OO design - is something that must be considered to address encapsulation properly. It's no use encapsulating a random collection of functions - responsibility needs to be cleanly and logically defined so that there is as little overlap or ambiguity as possible. For example, if we have a Toilet object we will want to wall off its domain of responsibilities from our Kitchen object.
In a limited sense, though, you are correct that a function, let's say, 'modularizes' some functionality by abstracting it. But, as I've said, 'encapsulation' as a term is understood in the broader context of object orientation to apply to a form of modularization that meets the three criteria listed above.
Sure it is.
For example, a method that operates only on its parameters would be considered "better encapsulated" than a method that operates on global static data.
Encapsulation has been around long before OOP :)
A method is no more an example of encapsulation than a car is an example of good driving. Encapsulation isn't about the synax, it is a logical design issue. Both objects and methods can exhibit good and bad encapsulation.
The simplest way to think about it is whether the code hides/abstracts the details from other parts of the code that don't have a need to know/care about the implementation.
Going back to the car example:
Automatic transmission offers good encapsulation: As a driver you care about forward/back and speed.
Manual Transmission is bad encapsulation: From the driver's perspective the specific gear required for low/high speeds is generally irrelevant to the intent of the driver.
No, objects aren't required for encapsulation. In the very broadest sense, "encapsulation" just means "hiding the details from view" and in that regard a method is encapsulating its implementation details.
That doesn't really mean you can go out and say your code is well-designed just because you divided it up into methods, though. A program consisting of 500 public methods isn't much better than that same program implemented in one 1000-line method.
In building a program, regardless of whether you're using object oriented techniques or not, you need to think about encapsulation at many different places: hiding the implementation details of a method, hiding data from code that doesn't need to know about it, simplifying interfaces to modules, etc.
Update: To answer your updated question, both "putting code in a method" and "using an access modifier" are different ways of encapsulating logic, but each one acts at a different level.
Putting code in a method hides the individual lines of code that make up that method so that callers don't need to care about what those lines are; they only worry about the signature of the method.
Flagging a method on a class as (say) "private" hides that method so that a consumer of the class doesn't need to worry about it; they only worry about the public methods (or properties) of your class.
The abstract concept of encapsulation means that you hide implementation details. Object-orientation is but one example of the use of ecnapsulation. Another example is the language called module-2 that uses (or used) implementation modules and definition modules. The definition modules hid the actual implementation and therefore provided encapsulation.
Encapsulation is used when you can consider something a black box. Objects are a black box. You know the methods they provide, but not how they are implemented.
[EDIT]
As for the example in the updated question: it depends on how narrow or broad you define encapsulation. Your AddOne example does not hide anything I believe. It would be information hiding/encapsulation if your variable would be an array index and you would call your method moveNext and maybe have another function setValue and getValue. This would allow people (together maybe with some other functions) to navigate your structure and setting and getting variables with them being aware of you using an array. If you programming language would support other or richer concepts you could change the implementation of moveNext, setValue and getValue with changing the meaning and the interface. To me that is encapsulation.
It's a component-level thing
Check this out:
In computer science, Encapsulation is the hiding of the internal mechanisms and data structures of a software component behind a defined interface, in such a way that users of the component (other pieces of software) only need to know what the component does, and cannot make themselves dependent on the details of how it does it. The purpose is to achieve potential for change: the internal mechanisms of the component can be improved without impact on other components, or the component can be replaced with a different one that supports the same public interface.
(I don't quite understand your question, let me know if that link doesn't cover your doubts)
Let's simplify this somewhat with an analogy: you turn the key of your car and it starts up. You know that there's more to it than just the key, but you don't have to know what is going on in there. To you, key turn = motor start. The interface of the key (that is, e.g., the function call) hides the implementation of the starter motor spinning the engine, etc... (the implementation). That's encapsulation. You're spared from having to know what's going on under the hood, and you're happy for it.
If you created an artificial hand, say, to turn the key for you, that's not encapsulation. You're turning the key with additional middleman cruft without hiding anything. That's what your example reminds me of - it's not encapsulating implementation details, even though both are accomplished through function calls. In this example, anyone picking up your code will not thank you for it. They will, in fact, be more likely to club you with your artificial hand.
Any method you can think of to hide information (classes, functions, dynamic libraries, macros) can be used for encapsulation.
Encapsulation is a process in which attributes(data member) and behavior(member function) of a objects in combined together as a single entity refer as class.
The Reference Model of Open Distributed Processing - written by the International Organisation for Standardization - defines the following concepts:
Entity: Any concrete or abstract thing of interest.
Object: A model of an entity. An object is characterised by its behaviour and, dually, by its state.
Behaviour (of an object): A collection of actions with a set of constraints on when they may occur.
Interface: An abstraction of the behaviour of an object that consists of a subset of the interactions of that object together with a set of constraints on when they may occur.
Encapsulation: the property that the information contained in an object is accessible only through interactions at the interfaces supported by the object.
These, you will appreciate, are quite broad. Let us see, however, whether putting functionality within a function can logically be considered to constitute towards encapsulation in these terms.
Firstly, a function is clearly a model of a, 'Thing of interest,' in that it represents an algorithm you (presumably) desire executed and that algorithm pertains to some problem you are trying to solve (and thus is a model of it).
Does a function have behaviour? It certainly does: it contains a collection of actions (which could be any number of executable statements) that are executed under the constraint that the function must be called from somewhere before it can execute. A function may not spontaneously be called at any time, without causal factor. Sounds like legalese? You betcha. But let's plough on, nonetheless.
Does a function have an interface? It certainly does: it has a name and a collection of formal parameters, which in turn map to the executable statements contained in the function in that, once a function is called, the name and parameter list are understood to uniquely identify the collection of executable statements to be run without the calling party's specifying those actual statements.
Does a function have the property that the information contained in the function is accessible only through interactions at the interfaces supported by the object? Hmm, well, it can.
As some information is accessible via its interface, some information must be hidden and inaccessible within the function. (The property such information exhibits is called information hiding, which Parnas defined by arguing that modules should be designed to hide both difficult decisions and decisions that are likely to change.) So what information is hidden within a function?
To see this, we should first consider scale. It's easy to claim that, for example, Java classes can be encapsulated within a package: some of the classes will be public (and hence be the package's interface) and some will be package-private (and hence information-hidden within the package). In encapsulation theory, the classes form nodes and the packages form encapsulated regions, with the entirety forming an encapsulated graph; the graph of classes and packages is called the third graph.
It's also easy to claim that functions (or methods) themselves are encapsulated within classes. Again, some functions will be public (and hence be part of the class's interface) and some will be private (and hence information-hidden within the class). The graph of functions and classes is called the second graph.
Now we come to functions. If functions are to be a means of encapsulation themselves they they should contain some information public to other functions and some information that's information-hidden within the function. What could this information be?
One candidate is given to us by McCabe. In his landmark paper on cyclomatic complexity, Thomas McCabe describes source code where, 'Each node in the graph corresponds to a block of code in the program where the flow is sequential and the arcs correspond to branches taken in the program.'
Let us take the McCabian block of sequential execution as the unit of information that may be encapsulated within a function. As the first block within the function is always the first and only guaranteed block to be executed, we can consider the first block to be public, in that it may be called by other functions. All the other blocks within the function, however, cannot be called by other functions (except in languages that allow jumping into functions mid-flow) and so these blocks may be considered information-hidden within the function.
Taking these (perhaps slightly tenuous) definitions, then we may say yes: putting functionality within a function does constitute to encapsulation. The encapsulation of blocks within functions is the first graph.
There is a caveate, however. Would you consider a package whose every class was public to be encapsulated? According to the definitions above, it does pass the test, as you can say that the interface to the package (i.e., all the public classes) do indeed offer a subset of the package's behaviour to other packages. But the subset in this case is the entire package's behaviour, as no classes are information-hidden. So despite regorously satisfying the above definitions, we feel that it does not satisfy the spirit of the definitions, as surely something must be information-hidden for true encapsulation to be claimed.
The same is true for the exampe you give. We can certainly consider n = n + 1 to be a single McCabian block, as it (and the return statement) are a single, sequential flow of executions. But the function into which you put this thus contains only one block, and that block is the only public block of the function, and therefore there are no information-hidden blocks within your proposed function. So it may satisfy the definition of encapsulation, but I would say that it does not satisfy the spirit.
All this, of course, is academic unless you can prove a benefit such encapsulation.
There are two forces that motivate encapsulation: the semantic and the logical.
Semantic encapsulation merely means encapsulation based on the meaning of the nodes (to use the general term) encapsulated. So if I tell you that I have two packages, one called, 'animal,' and one called 'mineral,' and then give you three classes Dog, Cat and Goat and ask into which packages these classes should be encapsulated, then, given no other information, you would be perfectly right to claim that the semantics of the system would suggest that the three classes be encapsulated within the, 'animal,' package, rather than the, 'mineral.'
The other motivation for encapsulation, however, is logic.
The configuration of a system is the precise and exhaustive identification of each node of the system and the encapsulated region in which it resides; a particular configuration of a Java system is - at the third graph - to identify all the classes of the system and specify the package in which each class resides.
To logically encapsulate a system means to identify some mathematical property of the system that depends on its configuration and then to configure that system so that the property is mathematically minimised.
Encapsulation theory proposes that all encapsulated graphs express a maximum potential number of edges (MPE). In a Java system of classes and packages, for example, the MPE is the maximum potential number of source code dependencies that can exist between all the classes of that system. Two classes within the same package cannot be information-hidden from one another and so both may potentially form depdencies on one another. Two package-private classes in separate packages, however, may not form dependencies on one another.
Encapsulation theory tells us how many packages we should have for a given number of classes so that the MPE is minimised. This can be useful because the weak form of the Principle of Burden states that the maximum potential burden of transforming a collection of entities is a function of the maximum potential number of entities transformed - in other words, the more potential source code dependencies you have between your classes, the greater the potential cost of doing any particular update. Minimising the MPE thus minimises the maximum potential cost of updates.
Given n classes and a requirement of p public classes per package, encapsulation theory shows that the number of packages, r, we should have to minimise the MPE is given by the equation: r = sqrt(n/p).
This also applies to the number of functions you should have, given the total number, n, of McCabian blocks in your system. Functions always have just one public block, as we mentioned above, and so the equation for the number of functions, r, to have in your system simplifies to: r = sqrt(n).
Admittedly, few considered the total number of blocks in their system when practicing encapsulation, but it's readily done at the class/package level. And besides, minimising MPE is almost entirely entuitive: it's done by minimising the number of public classes and trying to uniformly distribute classes over packages (or at least avoid have most packages with, say, 30 classes, and one monster pacakge with 500 classes, in which case the internal MPE of the latter can easily overwhelm the MPE of all the others).
Encapsulation thus involves striking a balance between the semantic and the logical.
All great fun.
in strict object-oriented terminology, one might be tempted to say no, a "mere" function is not sufficiently powerful to be called encapsulation...but in the real world the obvious answer is "yes, a function encapsulates some code".
for the OO purists who bristle at this blasphemy, consider a static anonymous class with no state and a single method; if the AddOne() function is not encapsulation, then neither is this class!
and just to be pedantic, encapsulation is a form of abstraction, not vice-versa. ;-)
It's not normally very meaningful to speak of encapsulation without reference to properties rather than solely methods -- you can put access controls on methods, certainly, but it's difficult to see how that's going to be other than nonsensical without any data scoped to the encapsulated method. Probably you could make some argument validating it, but I suspect it would be tortuous.
So no, you're most likely not using encapsulation just because you put a method in a class rather than having it as a global function.
I have some event handler on a boundary class that manages a persistence mechanism for a given generic transaction:
void MyBoundaryClass::MyEventHandler(...)
{
//retrieve stuff from the UI
//...
//declare and initialize trasaction to persist
SimpleTransaction myTransaction(.../*pass down stuff*/);
//do some other checks
//...
//declare transaction persistor
TransactionPersistor myPersistor(myTransaction, .../*pass down connection to DB and other stuff*/);
//persist transaction
try
{
myPersistor.Persist();
}
catch(...)
{
//handle errors
}
}
Would it be better to have some kind of TransactionManager to wrap SimpleTransaction and TransactionPErsistor objects?
Is there any useful rule of thumb to understand if I need a further level of encapsulation?
At the moment the rule of thumb I follow is "if the method gets too big - do something about it". It is hard sometimes to find the right balance between procedural and object oriented when dealing with boundary event handlers.
Any opinion?
Cheers
Considering that:
the concept of encapsulation is about defining a container, and
object-oriented design is based on the concept of message passing (invocation of methods)
I would argue that the API is a good indication about the pertinence of a new high-level encapsulation (I.e. the definition of a new object)
If the services (i.e the API) offered by this new object are coherent, and are better exposed to the rest of the program when regrouped in one special object, then by all means, use a new object.
Otherwise, it is probable an overkill.
Since you expose a public API by creating a new object, the notion of test may be easier to do within that new object (and a few other mock objects), rather than create many legacy objects in order to test those same operations.
In your case, if you want to test the transaction, you must actually test MyEventHandler of MyBoundaryClass, in order to retrieve data from the UI.
But if you define a TransactionManager, that gives you the opportunity to lower coupling of different architecture levels (GUI vs. data) present in MyBoundaryClass, and to export data management into a dedicated class.
Then, you can test data persistence in independent test scenario, focusing especially on limit values, and database failure, and not-nominal conditions, and so on.
Testing scenario can help you refine the cohesion (great point mentioned by Daok) of your different objects. If your tests are simple and coherent, chances are that your objects have a well-define service boundary.
Since it can be argued that Coupling and Cohesion are two cornerstones of OO Programming, the cohesion of a new class like TransactionManager can be evaluated in term of the set of actions it will perform.
Cohesive means that a certain class performs a set of closely related actions. A lack of cohesion, on the other hand, means that a class is performing several unrelated tasks. [...] the application software will eventually become unmanageable as more and more behaviors become scattered and end up in wrong places.
If you regroup behaviors otherwise implemented in several different places into your TransactionManager, it should be fine, provided that its public API represent clear steps of what a transaction involves and not "stuff about transaction" like various utility functions. A name in itself is not enough to judge the cohesiveness of a class. The combination of the name and its public API is needed.
For instance, one interesting aspect of a TransactionManager would be to completely encapsulate the notion of Transaction, which would :
become virtually unkown by the rest f the system, and would lower coupling between the other classes and 'Transaction'
reinforce the cohesiveness of TransactionManager by centering its API around transaction steps (like initTransaction(), persistTransaction(), ...), avoiding any getter or setter for any Transaction instance.
Elaborating on VonC's suggestion, consider the following guidelines:
If you expect to invoke the same functions elsewhere, in the same way, it's reasonable to encapsulate them in a new object.
If one function (or one object) provides a set of facilities that are useful individually, it's reasonable to refactor it into smaller components.
VonC's point about the API is an excellent litmus test: create effective interfaces, and the objects often become apparent.
The level of encapsulating should be directly linked to the cohesion of your object. Your object must do a single task or must be divided in multiple class and encapsulate all its behaviors and properties.
A rule of thumb is when it's time to test your object. If you are doing Unit Testing and you realize that you are testing multiple different thing (not in the same area action) than you might try to split it up.
For you case, I would encapsulate with your idea of "TransactionManager". This way the "TransactionManager" will handle how transaction works and not "MyBoundaryClass".
This is a question with many answers - I am interested in knowing what others consider to be "best practice".
Consider the following situation: you have an object-oriented program that contains one or more data structures that are needed by many different classes. How do you make these data structures accessible?
You can explicitly pass references around, for example, in the constructors. This is the "proper" solution, but it means duplicating parameters and instance variables all over the program. This makes changes or additions to the global data difficult.
You can put all of the data structures inside of a single object, and pass around references to this object. This can either be an object created just for this purpose, or it could be the "main" object of your program. This simplifies the problems of (1), but the data structures may or may not have anything to do with one another, and collecting them together in a single object is pretty arbitrary.
You can make the data structures "static". This lets you reference them directly from other classes, without having to pass around references. This entirely avoids the disadvantages of (1), but is clearly not OO. This also means that there can only ever be a single instance of the program.
When there are a lot of data structures, all required by a lot of classes, I tend to use (2). This is a compromise between OO-purity and practicality. What do other folks do? (For what it's worth, I mostly come from the Java world, but this discussion is applicable to any OO language.)
Global data isn't as bad as many OO purists claim!
After all, when implementing OO classes you've usually using an API to your OS. What the heck is this if it isn't a huge pile of global data and services!
If you use some global stuff in your program, you're merely extending this huge environment your class implementation can already see of the OS with a bit of data that is domain specific to your app.
Passing pointers/references everywhere is often taught in OO courses and books, academically it sounds nice. Pragmatically, it is often the thing to do, but it is misguided to follow this rule blindly and absolutely. For a decent sized program, you can end up with a pile of references being passed all over the place and it can result in completely unnecessary drudgery work.
Globally accessible services/data providers (abstracted away behind a nice interface obviously) are pretty much a must in a decent sized app.
I must really really discourage you from using option 3 - making the data static. I've worked on several projects where the early developers made some core data static, only to later realise they did need to run two copies of the program - and incurred a huge amount of work making the data non-static and carefully putting in references into everything.
So in my experience, if you do 3), you will eventually end up doing 1) at twice the cost.
Go for 1, and be fine-grained about what data structures you reference from each object. Don't use "context objects", just pass in precisely the data needed. Yes, it makes the code more complicated, but on the plus side, it makes it clearer - the fact that a FwurzleDigestionListener is holding a reference to both a Fwurzle and a DigestionTract immediately gives the reader an idea about its purpose.
And by definition, if the data format changes, so will the classes that operate on it, so you have to change them anyway.
You might want to think about altering the requirement that lots of objects need to know about the same data structures. One reason there does not seem to be a clean OO way of sharing data is that sharing data is not very object-oriented.
You will need to look at the specifics of your application but the general idea is to have one object responsible for the shared data which provides services to the other objects based on the data encapsulated in it. However these services should not involve giving other objects the data structures - merely giving other objects the pieces of information they need to meet their responsibilites and performing mutations on the data structures internally.
I tend to use 3) and be very careful about the synchronisation and locking across threads. I agree it is less OO, but then you confess to having global data, which is very un-OO in the first place.
Don't get too hung up on whether you are sticking purely to one programming methodology or another, find a solution which fits your problem. I think there are perfectly valid contexts for singletons (Logging for instance).
I use a combination of having one global object and passing interfaces in via constructors.
From the one main global object (usually named after what your program is called or does) you can start up other globals (maybe that have their own threads). This lets you control the setting up of program objects in the main objects constructor and tearing them down again in the right order when the application stops in this main objects destructor. Using static classes directly makes it tricky to initialize/uninitialize any resources these classes use in a controlled manner. This main global object also has properties for getting at the interfaces of different sub-systems of your application that various objects may want to get hold of to do their work.
I also pass references to relevant data-structures into constructors of some objects where I feel it is useful to isolate those objects from the rest of the world within the program when they only need to be concerned with a small part of it.
Whether an object grabs the global object and navigates its properties to get the interfaces it wants or gets passed the interfaces it uses via its constructor is a matter of taste and intuition. Any object you're implementing that you think might be reused in some other project should definately be passed data structures it should use via its constructor. Objects that grab the global object should be more to do with the infrastructure of your application.
Objects that receive interfaces they use via the constructor are probably easier to unit-test because you can feed them a mock interface, and tickle their methods to make sure they return the right arguments or interact with mock interfaces correctly. To test objects that access the main global object, you have to mock up the main global object so that when they request interfaces (I often call these services) from it they get appropriate mock objects and can be tested against them.
I prefer using the singleton pattern as described in the GoF book for these situations. A singleton is not the same as either of the three options described in the question. The constructor is private (or protected) so that it cannot be used just anywhere. You use a get() function (or whatever you prefer to call it) to obtain an instance. However, the architecture of the singleton class guarantees that each call to get() returns the same instance.
We should take care not to confuse Object Oriented Design with Object Oriented Implementation. Al too often, the term OO Design is used to judge an implementation, just as, imho, it is here.
Design
If in your design you see a lot of objects having a reference to exactly the same object, that means a lot of arrows. The designer should feel an itch here. He should verify whether this object is just commonly used, or if it is really a utility (e.g. a COM factory, a registry of some kind, ...).
From the project's requirements, he can see if it really needs to be a singleton (e.g. 'The Internet'), or if the object is shared because it's too general or too expensive or whatsoever.
Implementation
When you are asked to implement an OO Design in an OO language, you face a lot of decisions, like the one you mentioned: how should I implement all the arrows to the oft used object in the design?
That's the point where questions are addressed about 'static member', 'global variable' , 'god class' and 'a-lot-of-function-arguments'.
The Design phase should have clarified if the object needs to be a singleton or not. The implementation phase will decide on how this singleness will be represented in the program.
Option 3) while not purist OO, tends to be the most reasonable solution. But I would not make your class a singleton; and use some other object as a static 'dictionary' to manage those shared resources.
I don't like any of your proposed solutions:
You are passing around a bunch of "context" objects - the things that use them don't specify what fields or pieces of data they are really interested in
See here for a description of the God Object pattern. This is the worst of all worlds
Simply do not ever use Singleton objects for anything. You seem to have identified a few of the potential problems yourself