
How to design a class - SandB0x
http://stackoverflow.com/a/4203836/553404
======
azov
Let's try this advice. Say, we're solving Hanoi Towers [1]. So, we're going to
have rods, and disks, and rods are going to have the number of disks on them,
and disks are going to have size, and probably a reference to the disk
underneath, and a reference to the disk on top, and maybe also weight and
color... and by the end of the day instead of a 10-line snippet [2] we're
going to have Enterprise Java Beans.

No, it doesn't mean that OOP is awful - it just means that this particular way
of modeling a problem is a recipe for over-engineering.

Start with the simplest thing that works. That thing will probably be just a
function. Grow it from there. If it gets too big, split it. If you find that
you pass the same 12 parameters to a bunch of functions - factor out a class.
If you do the same thing in a bunch of classes - abstract it out. Keep it DRY
[3]. Keep it SOLID [4]. Rinse and repeat. This way you end up with a useful
class hierarchy - and OOP won't be awful.

[1]
[http://en.wikipedia.org/wiki/Tower_of_Hanoi](http://en.wikipedia.org/wiki/Tower_of_Hanoi)

[2]
[http://rosettacode.org/wiki/Towers_of_Hanoi](http://rosettacode.org/wiki/Towers_of_Hanoi)

[3]
[http://en.wikipedia.org/wiki/Don't_repeat_yourself](http://en.wikipedia.org/wiki/Don't_repeat_yourself)

[4] [http://en.wikipedia.org/wiki/SOLID_(object-
oriented_design)](http://en.wikipedia.org/wiki/SOLID_\(object-
oriented_design\))

~~~
aidos
I think that's a little too black and white back the other way.

Don't get me wrong, I live in python, I create classes on the odd occasion I
need them and not before it's definitely required.

That said, there's nothing wrong with sitting down and modelling the problem
you're trying to solve on paper to try to understand what's involved. You
don't need to implement it that way. Whatever you do, stop and take a moment
to think it through.

~~~
chaosphere2112
The thing that I keep finding myself wanting in Python is a more structured
way to refer to data; fortunately, that's what namedtuple[1] is for.

    
    
        >>> DataType = namedtuple("DataType", ["field1", "field2"])
        >>> datum = DataType(1, "a")
        >>> datum
        DataType(field1=1, field2='a')
        >>> datum.field1
        1
        >>> datum.field2
        2
    

Keeps my code a bit more sensible than constantly indexing into tuples, imo.

[1]:
[http://docs.python.org/2/library/collections.html#collection...](http://docs.python.org/2/library/collections.html#collections.namedtuple)

~~~
textminer
It was personally strange to start programming in earnest with Python, and
think namedtuples (or classes with __slots__) were so cool and efficient, and
then learn C and just think "oh yeah. Structs."

------
rayiner
Object oriented programming is awful, and this answer describes why. It shifts
focus from algorithms to objects. As a result, you get these over-designed
programs with lots of objects that have lots of methods, and the algorithm
gets totally obscured.

See: [http://steve-yegge.blogspot.com/2006/03/execution-in-
kingdom...](http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-
nouns.html).

~~~
groovy2shoes
Much of OOP and FP is very similar. In fact, closures and objects are
basically equivalent: each can be implemented with the other. So then the
differences between OOP and FP are largely a matter of style, and arguments
consist mostly of anecdotes.

But OOP has some concrete advantage over FP, and vice-versa. Specifically, OOP
makes it easier to add variants. If, at some point during the course of
programming, you decide you need a new variant, it's simple. You make a new
subclass. On the other hand, FP makes it easier to add operations. You just
write a function. So you wind up with a matrix:

    
    
                   | OOP    | FP
        ---------------------------
        Variants   | Easier | Harder
        Operations | Harder | Easier
    

This has been independently observed by several programming language
researchers. Then the question becomes, how can we make _both_ easier at the
same time? This is the Expression Problem. The Expression Problem is solvable
in several existing programming languages, but the solution is often ugly and
convoluted. So I like to add an additional restriction: how can we make both
easier _without_ lots of ugly, confusing boilerplate? Things like multimethods
and typeclasses get us part-way there, I think.

While I love FP, it's important to not write-off OOP. There _are_ some
problems which are more naturally solved by OOP. Good ideas are good,
regardless of associated paradigm. Then, OOP should not be an object of
disdain, but an object of inspiration: how can we take all of the good parts
and remove all of the bad parts? How can we advance the art and power of
programming?

If we want to see improvements, we have to keep an open mind.

~~~
tel
After a recent discussion on this topic on the Haskell Reddit I posted that
I've begun to the see the Expression Problem in terms of data and codata [1].

In very brief, data tends to be completely transparent in its construction,
finite (or tending toward finiteness), possessing obvious structural equality,
and can be easily transformed in myriad ways via induction.

Codata on the other hand comes from the other side. It is completely
transparent in its destruction (observation, think object properties), tends
to be infinite (think how the process of a state machine is infinite),
possesses obvious entity identity, and can be easily created in myriad ways
via coinduction.

In most programming settings, these are just perspectives as they can easily
coincide, but you can observe the difference when you program in a strongly
normalizing like Agda.

I feel that OOP has built most of its abstraction on the assumption that
codata-oriented (object-oriented, kind of) modeling works well. For this
reason, it's easy to talk about black-boxed domain models which are only
predictably observed and classified, but it gets hairy to talk about equality
of two trees---you're forced to think about pointer equality even when
structural equality might be a more natural fit.

FP doesn't necessarily pick one side or the other, but it certainly makes data
more accessible (algebraic data types being as terse as they can be) leading
to the Alan Perlis "100 functions" quote.

Greatly powerful things can come about when the two sides are combined or
emphasized for their strengths. In particular, the `pipes` ecosystem being
built in Haskell right now straddles the line by being perhaps best modeled as
a hybrid data/codata type. This means that you can both somewhat easily
construct pipes inductively while also observing the infinite process (codata)
they represent.

I don't think anything I'm saying is particularly new to researchers, but I
personally have been finding it incredibly instrumental when talking about how
OOP and FP differ.

[1]
[http://www.reddit.com/r/haskell/comments/1quhrl/from_object_...](http://www.reddit.com/r/haskell/comments/1quhrl/from_object_oriented_programming_to_functional/)

~~~
groovy2shoes
Interesting!

------
asveikau
I think this seems a little too existential. What is more important, that you
classified the right nouns and verbs in your Paws and Dogs, or that your code
works?

In my view OOP is all about sane interfaces between components. In your
internal code however, you should feel free to let your implementation details
and data structures drive the flow, and not con yourself into writing OO
spaghetti, that type of code written by inexperienced types who "heard
somewhere to use classes" and can only think in classes, and the abstraction
serves to obfuscate rather than clarify.

~~~
AlisdairSH
After years of software design, I can say with a high level of confidence that
classifying the nouns and verbs properly is a prerequisite to quality OO code
(from both a functional AND longevity standpoint).

If you fail to do that up-front analysis, you may end up with functional code.
But, the likelihood that it is readable, maintainable, extensible, etc is much
lower.

This isn't to say that all good code is OO. Only that writing good OO code
requires you to define the objects andt their interactions well.

~~~
CraigJPerry
Here framing the problem around a Walk class makes for a more elegant
implementation but the proscribed steps will never get you there.

------
ExpiredLink
Actually, only #1 and #2, "simple statements about what these objects will be
doing", are relevant. The 'nouns' points are misleading at best. OO is all
about behavior ('services'), not about data. A class doesn't encapsulate data,
it encapsulates state. That's a big difference that many OO aficionados don't
realize. Another source of confusion is that in some OO languages 'everything'
has to be a class. A Java class in many cases isn't a class in terms of OO.
Java Beans e.g. certainly are not OO classes.

------
ismarc
When I first used oop, or any object orientation, it was in the early days of
the public internet. There wasn't nearly the resources available now,
especially for a hobbyist. So when I learned the syntax (I believe it was
early c++), I had nothing else to go on. I naturally leapt to using objects to
describe and build new data types. It made me think about what data I needed,
then how the data needed to be operated on. It wasn't until I started taking
courses that I heard the noun approach... And it just seems completely
backwards. It isn't about modeling your data, it's about the conceptual items
you're working on. I still think oop is a good approach if you ignore all of
the 'best practices' and instead use it to build custom data types that
simplify your algorithms. It's part of what I really like about Erlang records
(even though they're a bit annoying to use) and Scala in general. You can
build custom data types without having to just compose lists and maps but you
get a lot of the benefits from functional programming.

I really don't understand why so much of the programming classes, tutorials,
guides and common wisdom aren't 'data first' style design.

------
jamesli
It is utterly ill-advised. Software design is about abstraction, to make code
modular, flexible, and extensible. It doesn't matter it is OOP or FP. Both
approaches have their own advantages and limitations.

The dogmatic answer is misleading. It might be ok to introduce OOP to new
programmers, to open a door for programmers who are only familiar with
procedural programming. For any experienced programmers, if they don't believe
in this dogma, I have to say this is not a right career for them.

------
Jare
This is almost word by word what I was taught about OOP in Introduction to
Programming back in 89. We used Modula-2, but it was already easy to connect
with the way we had been writing games in assembler. Later on, languages built
_for_ OOP like C++ or Java, with all the complexity and new features, shifted
the focus of teaching programming from the problem domain (what I need my code
to solve) to the language domain (what code I can write), and then Patterns
arrived, and things quickly got hairy and over-engineered and big.

------
zwieback
It's interesting to note that the question was "how to design a class", which
to me sounds like a question about static structure, e.g. what data members
and methods will the class have. Most of the discussion on this and the SO
page mixes in OOD, OOP with the original question and then throws in some
inappropriate OO vs. FP tangents.

It's important to remember that there's a lot of, for lack of a better word,
"class-oriented design" which typically tries to model the static structure of
the physical world or some artefacts of the programming environment.

In a completely separate world there's what I would call OO design and
programming which is more concerned about the interaction of actual objects
aka. instances of classes. Coming from the statically typed world the
distinction is often unclear but for the original Smalltalkers and other OO
purists object orientation was never about classes.

------
talles
Surprisingly not marked as 'too abroad' or something like that on SO...

I guess on 2010 people were more light on that

~~~
maaaats
Why does every thread featuring SO contain this knee-jerk response? If
anything, HN is the "bad" place with its constant nagging.

~~~
saraid216
Hi, my username is saraid216 and it has been 123 days since I quit Facebook.

...oh, wrong thread.

~~~
TylerE
Yes, but what's happened to your conversion rate?

------
jofer
And another one of Ivo's set of dog paw questions randomly become popular! The
guy has a knack for asking good questions and getting good answers.

~~~
ivoflipse
Thanks :-) Your answers on some of those other questions really helped me get
into this type of analysis. I'm even still working on my this dogs project
([https://github.com/ivoflipse/Pawlabeling](https://github.com/ivoflipse/Pawlabeling)),
the automatic labeling of the paws still needs a better solution!

------
spion
1\. First, write some use case scenarios/specs in order to get a better
understanding of the problem (for libraries this is README.md, for user-facing
programs its the user's problem and the solution's workflow)

2\. Write the code the way you would ideally want it to work, top-down.
Imagine that you're in fantasy land. Don't worry about it being wrong or
impossible to do that way. That will be corrected later.

Use classes to express blueprints for tiny worker machines that work on data,
not the data itself. Use classes to simulate entities.

A user is not the user's personal information - thats a data structure. A user
class makes sense in a testing framework where you want to simulate the
process of a user visiting a website.

A button is a worker machine that can detect a set of inputs and call attached
functions. A list is a worker machine that can keep items and update the
screen based on its scroll position. A stream is a worker that can be
customized to process incomming data packets in a certain way, and its output
can be connected to another stream.

Think of classes as tiny single-purpose computers or simulators.

3\. Express details about the desired outputs of that top-down code as tests.

4\. This is a good time to write all the _data structures_ to represent your
data. You can use relational modeling here even if you're not using a
relational database (or any kind of database), but you can also use
pointers/references instead of foreign keys.

Model the data structures for all data (including computed data), not just
input or stored data.

You can also do this before step 2. but in that case you will be tempted to
write your code to operate directly on the lowest level data structures,
instead of making it look clean and simple.

5\. Try to make the tests pass.

6\. If its impossible to implement or you realize its not a good idea to
implement it that way, tweak the test a bit then change your code and data
structures as you get a better understanding of the problem.

7\. Once you're satisfied with the test, you can stop.

8\. If the future of the program requires you to adapt it, keep working like
that over the existing code. The tests give you some reasonable assurance that
it still solves the old variant of the problem, while new tests will ensure
that the code sanely solves newer problems too.

------
yetanotherphd
The advice being given is more applicable to problems which have some real
state inherent in the problem, e.g. network connections, UI state or
databases.

In numerical programming, state is less obvious because it depends on how you
do the calculation.

In numerical programming, I tend to use a functional style that makes use of
objects. E.g. instead of

    
    
      y = f(params, x)
    

I would write

    
    
      y = my_object.f(x)
    

where my_object encapsulates params. This makes sense as the only function
that needs to access params is f. On the other hand Python has no private
members anyway so I would probably use the first way for Python anyway.

------
odonnellryan
I liked this a lot, but do we think all those steps are needed? I feel like it
can definitely be simplified, and that this is even scary for people new to
Python and OOP.

~~~
markkanof
I suspect that you have some amount of experience that allows you to just do
many of these things intuitively (possibly in your head without writing them
down). For a beginner, I think it can be helpful to have a detailed procedure
to follow that takes them from start to finish. That way they can focus mental
energy on the problem at hand while not having to worry that they may be
working through the problem using a method that is inefficient or that may
never get results.

------
xtr
Thanks, I also found this very beneficial.

------
djvu9
At first sight I thought it is sarcasm...

------
blahbl4hblahtoo
I really enjoyed this...the upvoted answer was really helpful. I'm going to
use it tonight when I get home and can work on a personal project.

