tomtees — 2010-06-06T18:17:10-04:00 — #1
Is it correct that Get and Set are called "Accessor Methods"?
Is there any reason why using them is bad?
(I remember reading something somewhere - maybe on here - that said there was an alternative approach to using Setters/Getters?)
jream — 2010-06-17T18:34:49-04:00 — #2
I've been a little frustrated on this, because I've been trying to keep a uniform system and passing down too many different types of return values from object to object can get tricky to remember down the line.
I have a public property that can be retrieved from a nested object, ie:
However, as mentioned above these could be changed outside of the class is not very good, so thank you for reminding me of a getter setter.
$Errors = $this->object->Errors();
$Output = $this->object->Output();
public function Errors()
Just tossing in my comments, sorry if it's now irrelevant but this just refreshed my memory and I think that's great advice in this post.
wardrop — 2010-06-14T19:13:38-04:00 — #3
Some good points have been made throughout this discussion, regarding both object data and relationship handling, and exceptions.
Just a little tip. If in doubt, go with whatever is simplest. Simple is usually clean, readable, fast and easy to change later if you find the solution wasn't satisfactory. Exceptions as already mentioned, are a simple way to handle... exceptional circumstances (things which shouldn't happen). On the same note, getters and setters are generally speaking, not all that simple, and somewhat violate the constraints of the language (in this case, PHP).
I've said this more than once so there's no harm in saying it again. Designing a complex heavily-engineered system in respects to either programming or user interface design, is relatively easy. Simplifying that system down as much as possible, requires a much more skillful developer. In the 90's, dialog boxes filled with a 100 options and checkbox's were probably desirable. These days, you have to make decision for your users. No one knows more about the workings of your system than you do, and you should know how it should be used, so make as many decisions for the end user as you possibly can, leaving only the important and most potentially variable decisions up to the user.
lastcraft — 2010-06-13T15:25:15-04:00 — #4
Sorry, I was actually asking something very specific. I didn't explain it at all well.
I think my replacement code for the exception is not unreasonable. If anything it's rather stacked up against the exceptions case. Have you timed it with microtime? That was my request, for you to time it.
I actually did this (for the first time) before posting the above.
My code snippet with 33% exception rate ran about twice as slow with non exception code as with exceptions. A bit of poking around clocked a throw and catch as about the same as 4 function calls (about 2 method calls). Sorry, I'm on a different machine right now, so I don't have the code to post, but it's trivial.
In other words even in this pretty much worse case, exceptions were faster not slower.
When I dropped the exception rate, the code got faster even when I took all but one of the return checks out! A try block with no catch event cost nothing by my measurements, about 10% of a function call was the most I could measure. Far faster than an enclosing if block.
The statement "exceptions are slow" is a myth, and based on my little experiment, completely and utterly bogus. Avoiding them is almost certainly slowing your code down. It's only the utter irrelevance of such micro benchmarks that have stopped you noticing.
So let's ignore that.
The real reason for using exceptions is clean code. A method such as findX() expects a return value that you will use. You just want to go...
Neat, clear and uncluttered. Force your users to spray if/else blocks everywhere and they can say goodbye readability.
A good API follows the Samurai principle: "Return victorious or don't return at all".
In other words, don't return a value unless that's the job of the method. Don't return "out of band" error codes and inflict tedious if checks on your fellow developers. They will grow to hate you.
lastcraft — 2010-06-13T16:55:38-04:00 — #5
Ok, so the click path could be:
1) Home page.
2) Click "Aeroplanes".
3) Click "Learjet".
4) Should see "Passengers: 6".
I'll do the adding to cart and payment later.
1) How do the product listings get on the system?
disgracian — 2010-06-12T20:06:16-04:00 — #6
Depending on your language, the main alternative to throwing an exception is silently failing, often resulting in the rest of your code being awash with null checks.
I prefer exceptions. They are usually more informative and, as Marcus points out, rather than compensating by defensively programming through multiple layers and function calls, it's often better to let the problem bubble up to a part of the program that has the responsibility of dealing with that error.
For instance, my data access objects don't catch any database errors (can't connect to server, errors thrown from stored procedures, etc.) because how to handle the same type of error is a higher level decision. An INSERT/UPDATE statement that violates a foreign key restraint might be no big deal in some rare cases (just catch the error and move on to the next row) or an UPDATE statement that does not fail but affects 0 rows might be a catastrophic event and require an exception to be thrown manually.
So, for those reasons and more I prefer to deal with exceptions and catch them generally quite high up.
Also, I deal with a fair bit of poorly documented 3rd party libraries, and often letting things blow up in your face during testing is the only way to really get to know them.
tomtees — 2010-06-13T00:43:55-04:00 — #7
My design mimics any well developed e-commerce site like Amazon.com.
You land on the home page, see a search bar, categories off to the side, and featured items in the center.
Most people will choose a Category, then go to a "gallery" of related Products, then choose to see the "Product Details" for a given Product, then choose to Add to Cart, and so on...
Why do you ask??
lastcraft — 2010-06-12T19:04:18-04:00 — #8
Just one click path then. What do people see when they first visit the site? What's your most wanted action off of the home page?
disgracian — 2010-06-12T23:56:11-04:00 — #9
Thanks for bringing that up, Chroniclemaster1. That anti-pattern of the empty catch block is one of my biggest peeves. It's the modern equivalent of "ON ERROR RESUME NEXT", and anything that has its roots in VB cannot be a good thing.
If you're doing that, then it's a pretty big argument for dropping the try/catch block altogether and handling it further up the call stack where some intelligent decision can be made about what to do.
chroniclemaster1 — 2010-06-12T13:33:40-04:00 — #10
I think you are seeing some Thread Entropy here, maybe what's needed is a little explanation of WHY you do some of these things, because absolutely it's a balancing act.
Keep things private (Information Hiding): This is a good principle, and I usually start by making everything in my class private, then you up the access modifier the minimum amount necessary to accomplish whatever needs to get done.
The advantage of keeping things private is that code which lives outside of your class doesn't know how the class works. If you go in and change something later, does it matter to any code outside of your class? Well, if you're only changing things that are private the answer is a resounding NO. It didn't know what the hell was going on in the first place, and you've only touched stuff that it didn't know about. Therefore, the more stuff that stays private, the more stuff that stays flexible. If you have to change something that's protected, let's say the name of a protected member, you now have to look through any code which subclasses your class to see if you find the old name anywhere and update that too. If you change the name of a public member... you are now looking through your entire code base for any place that you consumed that class. It's not that you CAN'T do that, in fact if your program is going to be upgraded it will have to be, it's just a pain in the ass. This kind of thing is simply accepted on long term iterative development projects. The key is that making things private MINIMIZES how often we need to do this. So hiding your implementation is an extremely flexible practice. The flip side is that a class with ALL private members quite literally does NOTHING. Again, the real idea is that you want to minimize the public interface (the members, properties, functions, etc.) that people can see from the outside. Give other classes the things they need. Don't give other classes additional information, just because it might be convenient at the moment; it will bite you when you have to modify your code later.
Minimize class communication (Decoupling): You can see how this is just the flip side of Information Hiding. Information Hiding allows your class to guarantee that other classes don't become dependent on things you would really prefer they don't use; so you literally use "private" to forbid them to use it. Decoupling means writing your class so that it doesn't USE of other classes, at least no more than necessary.
Keep in mind that if you're class doesn't call ANY other class, then it is stuck with doing all the implementation itself. Encapsulation is the most powerful tool in programming because it allows you to hide implementation, so "Perfect Decoupling" comes at the price of not getting ANY benefits of Information Hiding. Here again, it's about what trade off makes the most sense to you.
Say you are creating an AuthenticationManager class to log users in. Do you have it get all the information from the UI? Probably not. Let the UI class handle those details and call you to manage the authentication parts. The AuthenticationManager can then assume it's first job is when someone calls its public Login() function and the username and password will be supplied as parameters. No need for AuthenticationManager to get involved in any of that.
Now we need to find out what the user's hashed password is from the database? Is that really authentication related? Data Access is more DAL level work, so call the DAL class which handles your users and pass in the user name; the DAL will hand you back the hashed password from the database. NOW we have coupled Authentication Manager to a function in the DAL, is this bad? Well, it means that someone (quite probably you) will have to update this code in AuthenticationManager if someone ever changes the interface of that DAL function. That's not a great thing and we would prefer not to if we don't have to. But in this case there's no other way to get that hashed password so effectively we HAVE to. Therefore I AM going to couple my AuthenticationManager to this DAL function because I want to be able to hide all the implementation of how the DAL function actually does everything it does. In this case, my desire for encapsulation and information hiding is trumping my desire for decoupling.
Now I have the password the user provided and the hashed password from the database. Obviously this does me NO good, I can't compare those two things. I need to hash the password the user provided. Am I going to write this code into the AuthenticationManage? If I do, I have excellent decoupling. However, here again, I would submit that there's nothing about an AuthenticationManager that specifically screams "I hash stuff." If this is a really small application and I want to combine the hashString() function into this class, then we should probably rename our class from AuthenticationManager to SecurityManager because it's no longer handling just authentication but cryptographic management as well. Our other option, is to encapsulate hashString() out to a new CryptographyManager class and couple ourselves to the hashString() function. Note that if we want to get any work done we are coupling to the hashString() function. Unless we're not going to accomplish anything (and our program will do nothing), we are only talking about WHERE we are coupling to. We can either depend on a function in our class or we can depend on a function in another class. Ideally, we would like the function to be in our class to limit the complexity of our application (fewer classes running around is better than many classes running around). However, if our classes our difficult to understand (like a hashString() function in an AuthenticationManager) or if our classes get too long or confusing then it's time to simplify things with another class.
How do you determine what to break out?? ABSOLUTELY look for the code you can pull out which will require the minimum amount of contact between the old and the new class, ie look for how you can decouple the new class most completely. Chances are this is the best way to create a usable abstraction as well.
Exceptions are expensive, so they're generally my 3rd choice. 1st, try to take care of it yourself. Work around the error; this may mean throwing out the results of an iteration and continuing, assuming a default value, etc. 2nd, terminate and see if the user can take care of it; this is standard practice for user input errors. There's no need to throw an exception, just return an error string telling the user what they need to do e.g. "I'm sorry, I can't create a new account for you without a valid email address, please try again." In best case scenarios, exceptions are only to handle issues that you believe will never come up in your application when it goes live. But then we live in the real world so you will find them sometimes.
Getters and Setters are ways to expose information or modify information. This means they couple one class to another and violate the principle of information hiding. Are they bad? No. Just make sure you use them only when you really need them. For example, I find very little objectionable about Getters. If you've created your classes to good and consistent abstractions, you have a very robust application architecture, and you should feel free to create getters for any "necessary" information that other parts of the application may need. ie don't create a field and a Getter function for it. Wait until another class needs it and then create a Getter method; you should probably consider whether that class really NEEDS the information in the first place too.
Setters, I'm absolutely more skeptical about. I'm perfectly willing to let other parts of the app LOOK at the first and last name fields of my User class. Should any of them be CHANGING it?? If I think twice before I create a Getter, I think AT LEAST three times before creating a Setter. When given the opportunity, I've actually created the admin for a website as a separate application on a separate subdomain to increase security for reasons that included this. If you don't need to admin the application, you can leave out a lot of Setters. You'll need those in the admin application, but not here.
chroniclemaster1 — 2010-06-12T22:51:16-04:00 — #11
Also, error handling tends to be so specific that it deals with gross mechanics and I wanted to talk about some general ways to deal with errors. The alternative as we ALL seen in other people's code is lots of....
which I think we can all agree is the antithesis of error handling. It's the officially unhandled error.
chroniclemaster1 — 2010-06-12T22:37:53-04:00 — #12
I completely agree. However, I did want to point out other alternatives to going with an Exception as your first choice. I've seen entire books (and all of MS's C# documentation) written with the attitude that throwing an exception IS error handling and that if you do not throw an exception in EVERY function at EVERY level, then you have not included error handling in your program. Both extremes are equally ludicrous. Sometimes error handling is as simple as filling an appropriate default string into a variable for output.
:lol: Yeah, and people ask me "Isn't it SO much more work to do custom OO programming?" Not when you choose wisely.
tomtees — 2010-06-12T16:11:08-04:00 — #13
I really like your detailed pararaphs on "Information Hiding" and "Decoupling". The way you explained things will definitely help me at a larger design level when I'm figuring out what classes to create, what each class should do, and if one class should talk to another.
And you did all of that in a polite fashion!
disgracian — 2010-06-11T18:32:32-04:00 — #14
You probably need to point out that you would have to be very careful what you pass to the visitor object: i.e., copies of private members unless they are immutable. Otherwise the visitor object could modify private data, and that's not good.
And that could run into performance issues if you have to copy large amounts of data.
tomtees — 2010-06-11T12:24:00-04:00 — #15
No, the term is "Thread Entropy"...
OO actually makes it easy to change stuff as you go. Right now you are at the window sill worrying if you should jump, not knowing you are about to land on a feather bed. Jump damn you.
I'm trying, just want to make a "calculated" first jump.
Can you tell us one of your use cases, one of the work flows you have worked out? Then we can post code.
Ha ha. My main use-case including all flows is over 600 lines, so that wouldn't be a good way to help me out.
I need help taking baby steps.
I'll have to ask some smaller more specific questions. (The Setter/Getter was just a side theoretical type question, I suppose.)
tony_marston — 2010-06-11T05:19:21-04:00 — #16
I DON'T use getters and setters. The reason for this is code efficiency. I deal in applications which access a lot of database tables, so I have a separate class for each table. These inherit from an abstract table class which contains all the code which is common to every database table. Each table has a series of columns, but rather than get/set them individually I leave them in an array so that I can move them around 'en bloc'. For example, when using a form to insert data into a table all the columns are presented in the $_POST array, so I insert the whole array using a single method call:
$dbobject = new $table;
$fieldarray = $dbobject->insertRecord($_POST);
That is much more efficient than unpicking each column by name from the $_POST array and inserting it individually.
As I use the MVC pattern I regard each database object as being the Model, and is instantiated and accessed by the Controller.
You should notice that the Controller does not use getters and setters for individual field/column names, therefore the same controller can be used on ANY database table. This in turn means that I now have a set of reusable controllers, one for each operation, rather than one for each table+operation. This gives me maximum reusability, and makes it easy to modify a single controller and have the changes echoed across a large number of tasks.
Some of you may yell at me and say "But that's not how it's done!", but who says that YOUR way is the ONLY way? Who says that YOUR way is the BEST way?
lastcraft — 2010-06-10T07:04:21-04:00 — #17
Actually what you are seeing is a progression.
Hats off to ScallioXTX for a clear explanation of why a setter method can be better than a raw member variable, but (sorry) it's incorrect to say it's bad not to use them.
The progression in this case is:
(1) Classes as data holders
(2) Classes as encapsulated data holders
(3) Tell don't ask
(4) I know what I'm doing so goodbye rules
The problem with 1 is that anything can change my internal values. I was caching that thank you very much, now what?
The problem with 2 is that although you cannot get near my innards, I was a pain to write and endlessly bureaucratic. Rules is rules, blah, blah.
The good thing about 3 is you realise that, if all I do is hang on to a value for someone else, what on earth am I doing with that value. I hand it over or do the work. Less code that way. Shorter is better.
If I know the downside in 4, I can still have public properties. If I'm an internal object that is only seen by a few classes and the semantics are pull stuff in and push stuff out, go use a public property. Maybe the semantics are that it actually is a "property". If I get stuck later I can dig my way out with __get(), right? If you really can get away with it, just use a variable. In code shorter is better...oh, I already said that.
In DSL land of 5, what you see is not what you get. I may look like an object with public members, but I have get()/set() secrets. Are my values in memcache? In Postgres? You'll never know. Write the code you want and let me do my magic.
Steps 1 - 5 are universal and known as the "Dreyfus model". 1 is novice (what do I do?), 2 is advanced beginner, where everything is rule fitting. 3 is competent, they really solve the problem. 4 is proficient, they look at the larger picture. 5 is expert, they are looking to change the game and create new rules. People are at different levels for different skills. You can often guess their level from their tone .
Advice like not using exceptions for validation is kind of level 2 ("why not?" says level 4). Trouble is you need to make the mistakes that a lot of this advice is trying to avoid, just so you can get to level 4. You are doing the right thing though. In having heard the nagging voices, when you do hit the problems you will have a solution at the ready.
Can you express these as test cases? If so then you have indeed done the hard part.
That's incredibly hard and an unrealistic goal. Not what you wanted to hear?
Object models rarely appear out of thin air. If the do, they are invariably wrong. You have to evolve them, usually as you code. OO actually makes it easy to change stuff as you go. Right now you are at the window sill worrying if you should jump, not knowing you are about to land on a feather bed. Jump damn you.
The book you are after is "Refactoring" by Martin Fowler. This will answer a lot of the questions you have already asked.
The main thing is to code a small part of your solution. Then a second bit. Don't write a third bit until you have rewritten the first two parts to be nice. Then write the third bit. Modify it 'til it's nice. Only then the 4th bit...you get the picture. Your object model will either emerge or you'll get stuck with a more specific question.
You so didn't
Can you tell us one of your use cases, one of the work flows you have worked out? Then we can post code.
lastcraft — 2010-06-12T19:28:23-04:00 — #18
Suppose not throwing an exception involves you in writing a boolean if/else check to avoid executing that code and return an error in the function. Suppose also that as the function returns, the callers must check for errors at each stage whether there was an error or not. Let's be generous and assume that the code has an error a third of the time. Now let's suppose that there are four such checks through the call stack. i.e. instead of one throw/catch at the appropriate place, you've had to process 4 boolean checks as it backs out of the function.
Which code is faster?
lemon_juice — 2010-06-09T05:01:48-04:00 — #19
Ok, let me give you an example. Suppose you have a shopping cart object and you want to add a new product to the cart. If you went the route of "getting and setting their internal properties" that would look like this:
$cart = new Cart;
$products = $cart->getProducts();
$products = $newProduct;
$total = $cart->getTotal();
$total += $newProduct->getPrice();
Now the same by "have them do stuff":
$cart = new Cart;
This example is extreme but should illustrate the point well. In the first example you are essentially manipulating the object's internal properties from the outside while in the second one you only tell the object what to do and the object itself is responsible for performing all other necessary subtasks like incrementing cart's total, etc. It's much easier to maintain if you don't have to remember about all these subtasks from all the places you add a product. And in fact, the calling code should not care about them.
lemon_juice — 2010-06-08T03:36:43-04:00 — #20
Then I would say - just make plans to the best of your current abilities and begin work! Debates on design patterns can go on indefinitely with real experts having different opinions. Debating can be very time consuming - better to do your job and later you will be able to discuss the finer points or - you will discover the answers for yourself.
As to your original question I would suggest this: treat getters and setters as fully valid constructs but when designing objects keep the tendency to have them do stuff instead of getting and setting their internal properties. I'm also learning OOP, I have already had some good experience with it but still I find there are a lot of patterns and ways to program I don't know of - it all has to come gradually because I don't think it's possible to grasp it all at once.
next page →