可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm trying to understand object orientation. I understand it a little bit of course, but sometimes I'm not 100% clear. How do you decide what should be turned into an object (small object part of another big whole object) or what is not worth being an object, or maybe it should be just a property of that big whole object?
For a door, I guess the door knob should be an independent object, but should that part in the middle where you insert the key also be an independent object or what? This is a simple example so I can explain my confusion. You can use your example if it helps you make your point better.
I was thinking that if maybe I'm going to use it multiple times, I should make it an object. This I think is a practical way to resolve this problem, do you agree?
Thanks
回答1:
As always, the answer is unfortunatly: it depends...
At the beginning, you have some sort of environment and you want to create a model for it. But you would not model every thing, you'll concentrate on the important things. That's why I started with: it depends. It depends on what details you need to accomplish the task.
Take the car and it's wheels - if you model a city and want some traffic, you may create a 'Car' class with the attribute 'numberOfWheels'. But if you design cars, then you most likely want to create a class 'Wheel' aswell and add four of those to the 'Car' class.
Rule of a thumb:
- if that, what you're thinking about, has more then one attribute, define a class
- if that, what you're thinking about, has some behavior, define a class
- if that, what you're thinking about, may grow or be extended, define a class
- if that, what you're thinking about, has to be sortable, comparable, define a class
- if you're unsure, define a class ;)
Edit
Because you emphasized the 'multiple usage' aspect: I don't think that this is an aspect for a decision whether to use a class or not. Think of a simple counter, an integer value in a for loop. You'll use this concept hundreds of times but I bet you'll never think of wrapping this poor little int
into a 'Counter' class - just because you use the 'concept of a counter' multiple times.
回答2:
First: forget about physical objects. I know that all of the textbooks "learning examples" use them, but you're only going to confuse yourself when you try to model a real system. There is no direct relationship between physical objects and Objects.
An Object is a data structure combined with a set of methods that can operate on that data structure. The Properties of the Object hold the object state, and the methods operate on the state.
What is the system state you are trying to model? Does that reside in the door, the knob, or some combination of the two?
There are some methodologies that attempt to get the object model clearly specified before coding begins. Other methodologies (e.g, TDD) allow the object model to emerge through the coding process. In your case, I'd recommend coding some small-to-medium sized applications using TDD, so that you can see the advantages and disadvantages to various patterns. There's rarely one "right" way to model a given situation, but some models are much easier to use and understand than others; recognizing the patterns that apply to the situation in front of you comes with experience.
So, bottom line: make a lot of models. Think of application domains, model them, and code. In doing so, things will become much clearer. Make a sandbox, and jump in.
回答3:
When deciding if something is an object or not, ask yourself if it has the following ...
State
Does the candidate have significant state? If it doesn't then all of the methods on it are likely to be only weakly related. In this case you've probably identified a library of module of reusable functions.
Behaviour
Does the object actually do something in the domain? For example is it just full of accessors which manipulate a struct or record.
Identity
Does the object really exist within the domain as an identifiable entity? Is there some innate aspect of the entity which distinguishes it from similar other entities? The door handle is an example - it doesn't really have identity since one door handle is likely to be the same as another.
If you answer no to some of these then you probably don't have an object - and that's fine a library or module can be a valuable reusable artifact
Above all, don't worry about it ...
I also wouldn't worry too much about the reuse aspect of this. Designing class hierarchies is like designing scalable software - it's too easy to optimise early. Sketch out a design on a piece of paper and if you can, validate the design with a few hand-drawn interaction diagrams. You'll find that over time you'll develop a real insight into what's really valuable and reusable.
回答4:
This depends a bit on the usage. To go with your example, if the doorknob is an important part and could (potentially) be used on another door (or a different knob be used for 'this' one) then it should probably be an object.
If on the other hand it is only there to allow you to open and close the door, it should simply be a part of the door object.
回答5:
As the old cliche says "objects should be nouns". Any time you find yourself thinking of a thing, it should probably be an object. Any time you find yourself thinking of an action, it should probably be a function or method.
Of course, there are exceptions to the above rules, and reality can be a bit more complex. However, this is probably the best starting place to start wrapping your head around the concept.
回答6:
The textbook way of deciding on object granularity is to go by cohesion.
If most of the methods of your object operates on most of the object's fields, then the object is small enough (For a given value of "most").
An object is almost never too small.
回答7:
I'd advise you to think about how you are going to use it. Think what operations you will have to do with your "thing" and then consider what would be the easiest way of doing it. Sometimes it will be easier to make it a property of another object, sometimes it will be easier to make it a new object of its own.
There is no universal recipe, there can be many factors that make one solution the better one. Just consider each case separately.
Added: To take your example about a door/doorknob/keyhole - what will you do with the keyhole? Here are some factors that would make it logical to make the keyhole as a separate object:
- You want store many properties of the keyhole like size, shape, direction, count of pins, whether the key can be turned once or twice, etc;
- There can be more than one keyhole on a doorknob;
- You want to give the keyhole some read-only properties (like whether it's locked or not) and then only allow modifying them through specific methods (like an "unlock" method which takes a "key" object as a parameters);
- Keyholes are stored in a separate DB table with one keyhole per DB row (then it might make sense to make the keyhole object mimic the DB structure);
- There are methods in your system that would be implemented in an elegant way if they could take a keyhole as a parameter;
The scenarios for making it a property are the opposite:
- Each doorknob can have only one keyhole and it has only one or very few properties;
- Keyholes are stored together with the doorknobs in the DB;
- You usually don't care about the keyhole alone, you just want it as a descriptive property of the doorknob (like whether or not there is one);
回答8:
It is an object if you need to think about it as... an object.
That is, if you need an abstraction.
The "key hole" in your example can be described on different levels of abstraction and only the last one from the list you would probably call "an object":
1) Could be a boolean
property, if you just need to know that your door has it:
class Door
{
bool HasKeyHole;
}
2) Could be a pair of coordinates, if you just want to draw your door and put a circle in place of the key hole
class Door
{
Point KeyHoleCoordinates;
}
3) Could be a specially defined class KeyHole
if you want to encapsulate logic and some properties of a key hole and work with them together, probably passing them around, or allowing interaction with a Key
class KeyHole
{
Point Coordinates;
bool OpensWithKey(Key key);
}
class Door
{
KeyHole Hole;
}
回答9:
You should also think about how many of the sub-objects you need. A door has one handle not four or five and not a list of handles. Also do the sub-objects have properties of their own? Is the color or the material important? Then better keep the handle as a separate object.
回答10:
Another way to look at it is whether or not the doorknob is interchangeable. I would make the doorknob a separate object that is a property on the door. One question is whether or not you want to make the doorknob a private class if only the door can have that knob. I personally prefer not use a private class here, but is a legitimate possibility. By using a separate object as a property on the door, you can now move that instance of the knob from one door (instance) to another (like exchanging knobs from one door to another in your house).
Another interesting aspect of this is extending your hierarchy... You might have fancy knobs, lockable knobs, etc, which can all be implemented by extending your base door knob.
I hope that helped a little to clarify your confusion.
回答11:
As long as the object has only one purpose/responsibility is should not be divided anymore (unless it is too large which should not be the case).
Make objects as long as you can divide and conquer them after that. If you make to many small objects you will not be able to handle well all. On the other other side to few big objects cannot be reused easily.
My advice: practice! While practicing you will get a sense of what granularity you need - there is no general rule for this.
回答12:
One way of finding out when you need to create an object and when not is to write down a short description of what you are trying to accomplish in plain language. If you are happy that you managed to express the problem in your description you can then pick the objects from that text as candidate classes. Then remove those that are obviously not needed.
Of course you will usually add many more classes to your SW and you will still remove some that you chose this way, but this is a starting point that I have used often. I usually end up drawing a crude ER diagram after this which further clarifies which classes I need. I also look at the candidate classes for similarities for inheritance purposes.
So if you feel the need to explain the keyhole in your short description then it's a good candidate for a class. If not, it might later still become apparent that you need a separate class for it but at that point you should already have a good idea of what you are doing in any case.
回答13:
There is theory and then there is practice... and then there is you as a software engineer trying to balance them both.
In theory you should make objects pretty much everything until you fall down to the smallest possible elements, the primitive types (boolean, string, integer etc). Well.. it is rather simplistic but with this one rarely goes wrong...
that is...
until you actually get to create (that is code the classes) the thing.
On the practical end of the spectrum you can define all in one big class and be done with it... that is... until you have to define subtle change in behavior (outside door, garage door, dog door etc).
My approach is to usually start with the one big class (ugly but it's fast to code and I can get a working prototype faster). Then if I ever need to define ajustments or new behaviour or reuse some part of the whole then I create the smalle element and refactor the big one to use the smaller one instead of defining it's own attributes.
For example. I code the door class, and from there I can create (instantiate) as many door as I want but they are all the same and behave the same. Now I realize that I need to also define windows that swivel around hinges... wait a minute... the door`s got hinges also. This is where I create a hinge class that can be used by both Door and Window and remove whatever way I had before to define a hinge in the door class. Then continue working until I encounter a situation where I can reuse some parts across multiple objects (the handle, the frame, etc).
- Never drill down the object until you have to but...
- Never duplicate code that can be reused.
With this rule of thumb I can get the code fast and usually it converges to a level of granularity that is sufficient for the needs at hand.
Then with experience you get to have a fair idea of on deep you want your objects granularity without having to constantly re-factoring your objects which is time consuming. However I found that the re-factoring is time consuming but never as much as designing the thing all the way down right from start. Re-factoring is almost inevitable, as such better to start re-factoring early and often.
anyways... my two cent, I hope it helps.
回答14:
In general, if you need more information from it than just only one thing (not only the knob's state, but also its color, its exact location, whether it has a key groove, the ability to change its state/behaviour, etc), then make it object. Thus, when you can't store all the information the door needs to know about the knob in a simple String
, Number
or Boolean
, then make it an fullworthy Knob
.
As everywhere you also have "corner cases". I see this often with pairs. Two propeties which are related to each other, but usually to nothing else. They aren't always grouped in a separate real world object. For example sortDirection
and sortField
. Whether they belongs in their own object depends on what the parent object represents. Is it a sortable implementation of List
? Okay, keep it there. Is it a Door
? Well, I would maybe externalize it.
回答15:
I answered this already in another question
Code objects are not related to tangible real-life objects; they are just constructs that hold related information together.
Don't believe what the Java books/schools teach about objects; they're lying.
Just write something that gets the job done, even if it's ugly, then refactor continuously:
- eliminate duplicate code (don't repeat yourself)
- increase cohesion
- reduce coupling
But:
- don't over-engineer; keep it simple
- don't write stuff you ain't gonna need
If you don't end up with massive (and useless) class hierarchy, then you have done a good job, producing elegant and clean code.
Remember: OOP is a means, not an end.
回答16:
Good old Plato already had an answer (kind of)...
But your question depends a lot on the language you're using, so there's no universal truth here. In some languages a thing should be a class, but it might be an object in others, or both (in those that have meta object protocols), or just a record of values and related functions.
回答17:
Everything can be made into an object.
IMO, the answer to your question is the question - Is the behaviour of the key-hole necessary for my model of the door to be an accurate representation?
When the answer to the above is in the affirmative, go ahead and incorporate it. When the answer to the same question is in the negative, then I'd choose not to.
回答18:
The door knob would be a separate object in most cases, that can be assigned to a door-object.
An excessive use of objects would say:
There's an object for the lock,
there is a color object for each color ("brownish" for the door, "silver" for lock and knob),
and material objects ("wood" for the door and "steel" for knob and lock)
Here you can see the difference between a class and an object:
A class is an abstract (not in the sense of the programming language) form of something.
You can refer something as a knob and everone knows what it is. You know that a knob has a color and a material.
If you buy a knob, you have a concrete knob-object in your hand, with a specific color and material. You can now change the color of you knob object, e.g. paint it black.
But there's a big diffenrence in programming objects and real-life objects.
These basic examples only help to understand basic principles of OOP. You should let loose of this very soon!
(For those who are curious: Is a rectangle a square or a square a rectangle?)
回答19:
Everything is an object. Sometimes you just use a primitive variable (int) to represent an object, and sometimes you create a data structure (struct/class). And naturally, some objects are parts of other objects.
So, yes, if you have to do in your code something with that part in the middle where you insert the key, it should also be an object in your code. It may be represented just by string Keyhole
and may be represented by a class Keyhole
, it may be an independent object and may be a part of the class DoorKnob
- but anyway it will be an object.
In order to decide, whether an object should be independent, or should it be a part of a bigger object, you can just ask: Is it needed in the context outside the bigger object, or is it just a part of the bigger object's implementation.
回答20:
Everything is an object. From quarks over electrons, atoms to elements, dust, men, cars, the world and the universe.
Even thoughts, ideas or feelings are objects.
(So far for the obvious answer)
Coming to "deciding what deservers to be an object"; I alawys to it as simple as does it have to behave in any way and will it be used more than once.
As soon as you use anything more than once, it's worth being a function or even an object.
Furthermore; will it be reused by other things? (Objects, Projects, Programs, etc.) These are the thoughts I have when I decide what should and what sould not be an object.
But, as I said above, the question is trivial, as everything is an object by itself.