Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Cohesive Software Engineering Talk
extends ./layout.jade
// This talk requires a combination of reveal.js, Jade, and some style sheets I made to support it.
block slides
section
strong Highly Cohesive Software Programming
section
p let's pretend you're a software engineer
// Fragments are revealed on slide next
p.fragment Senior Jawa Developer
+notes.
Let's pretend you're a software engineer
Let's say I have a mythical language called Jawa,
section
// Dimafter fragments are hidden after being shown
p.fragment.dimafter new feature
section
p.fragment.dimafter a service to upload translations to ci
p.fragment ci-3po
section
p.fragment.dimafter awesome.
p.fragment.dimafter Your teammate just finished a tool to convert resources into d3 visualizations in jawa
p.fragment r2d3
+notes.
and they push it up
section
p let's make them work together
img.fragment.noborder(src='resources/c3po_r2d2_1.gif')
p.fragment oh dear
+notes.
It turns out, r-2-d3 and ci-3po were both mutating the same file resources
you see, r2d3 was built unextensibly
section
p They're tangled together
div.fragment
img.noborder(src='resources/r2po.png')
+notes.
you have to hack them apart
section
p Programming can be like wiring up a switchboard
img(src='resources/switchboard.jpg', height='300', class='noborder')
section
img(src='resources/switchboard_3.jpg', height='300', class='noborder')
+notes.
If two people really far away keep calling each other, it'd get pretty tiring to wire them up
section
img(src='resources/switchboard_2.jpg', height='300', class='noborder')
+notes.
And can you imagine, if every time you had to call someone, you had to call another operator?
section
p `Cohesion`: Nearby things are related
section
p `Tight Coupling`: Unrelated things cannot be separated
section
p Cohesion is a spectrum
p.fragment has 2 sides, light and dark
+notes.
Extensibility is a spectrum.
You can go all in on extensibility, or you can make
it impossible for others to play in your walled garden
section
p High Cohesion
// replaceafter fragments are hidden and removed from the css (so the next element is shown where it was)
p.fragment.replaceafter Enables MVC
p.fragment.replaceafter Minimizes changes to code
p.fragment.replaceafter Makes things easier to test
p.fragment Enables Separation of Concerns
section
p High Cohesion: Make things easy to understand
p.fragment.replaceafter
img.noborder(src='resources/xkcd_star_wars.png', width='900')
em source: http://xkcd.com/657/
p.fragment
img.noborder(src='resources/xkcd_primer.png', width='900')
em source: http://xkcd.com/657/
+notes
p star wars may seem complicated,
p but really, it has many characters that
p come together occasionally, and then have their own story
p compared to primer, with very few characters, is nearly incomprehensible
p a movie, about time travel
section
p Overview
ul
li.fragment.dimafter "object oriented" programming
li.fragment.dimafter cohesiveness?
+notes.
section
p What is object oriented Programming, anyway?
section
p OO, the great pancea?
section
Most people misunderstand what it is
section
Most schools start out with something like this
+codeexample
| class Point {
| final int x, y
|
| public int getX() { return x }
| ...
section
p lisp
+codeexample
|
| (lambda () x)
|
+notes.
Not too different from a lambda function in a functional language
section
+codeexample
|
| (lambda (var)
| (cond (= var "x")
| x
| y))
|
+notes.
Ok, maybe one that determines if it should return x or y
section
p C
+codeexample
|
| point->x
|
section
p ok, does mutation help?
+codeexample
| class Point {
| int x, y
|
| public void setX(int x) { this.x = x }
| ...
+notes.
Easily represent as a struct in a lower order language
If no mutation
section
p C
p point->x = 5
section
p Besides: Immutability ain't needed
+codeexample
| class Point {
| int x, y
|
| public Point setX(int newX) {
| new Point(newX, y)
| }
| ...
+notes.
Value objects, transformed into themself
section
+codeexample
| class IntPoint implements Point {
| int x, y
|
| public int getX() { return x }
| ...
+notes.
Ok, what about when you start representing abstractions?
section
+codeexample
| class DoublePoint implements Point {
| double x, y
|
| public int getX() { return (int)x }
| ...
+notes.
Now you can use the same APIs with different underlying representation
section
p A good example?
+notes.
is that really a good example though? In this case it's just
data that is varying, not behavior
section
+codeexample
| class Image implements Drawable {
| int currentFrame = ...
|
| public void draw(Graphics g) { ... }
| ...
+notes.
Let's use a behavioral abstraction
section
+codeexample
| class Gif implements Drawable {
| int currentFrame = ...
|
| public void draw(Graphics g) { ... }
| ...
+notes.
Now you can represent new abstractions
section
An object is a first-class, dynamically dispatched behavior.
+notes.
http://wcook.blogspot.com/2012/07/proposal-for-simplified-modern.html?m=1
dynamic dispatch in C: http://www.cs.rit.edu/~ats/books/ooc.pdf
http://lwn.net/Articles/444910/
section
p Dynamic dispatch is sometimes called
ul
li.fragment.dimafter "message passing"
li.fragment.dimafter "late binding"
li.fragment.dimafter "dynamic binding"
li.fragment.dimafter "polymorphism"
+notes.
Polymorphism is what java devs are used to hearing
section
p Optional, but #[strong not essential:]
ul
li.fragment mutable state
li.fragment inheritance
li.fragment classes
li.fragment.fragment identity
section
p Mutable state
+notes.
Compose a program of value objects, state changes
through creation of new objects
section
+codeexample
| class Gif implements Drawable {
| int currentFrame = ...
|
+codeblock
| public Pair<Frame, Drawable> draw() {
| return new Pair(
| getFrame(),
| new Gif(currentFrame++));
| }
| ...
+notes.
We can create the same Gif by returning
a frame of the image, no mutation required
section
p inheritance
+notes.
composition can be isometric
Go: Dynamic dispatch
In java, inheritance is a means to achieve dispatch, so essential,
but extends is not:
section
+codeexample
| interface Drawable {
| public void draw(Graphics g);
| }
section
Others might pass around that thing
+codeexample
| public void setDrawable(Drawable d);
+notes.
Must be something that extends that type
section
p Inherits interface, can be used as type:
+codeexample
| public class Gif implements Drawable {
| public void draw(Graphics g) {
|
section
p Does not inherit interface, can't be used
+codeexample
| public class Artist {
| public void draw(Graphics g);
|
+notes.
Artist doesn't extend the interface, so can't be used.
You could wrap this artist, but there's a lot of downsides there
section
p Go: Interfaces are protocols
+codeexample
| type Drawable interface {
| Draw()
| }
p
em anything with Draw can be used by anything needing a Drawable
+notes.
Languages like go can define new protocols / interfaces
Existing types didn't inherit them explicitly.
section
p you don't need inheritance in all languages to do polymorphism
section
p identity, e.g., ==
+notes.
Compare by reference
section
p a.id.equals(b.id)
+notes.
If this is needed for objects, can be added to specific implementation
if things are using this explicitly, it is tightly coupling and preventing
dynamic dispatch on equals, so may cause issues.
section
p Objects are useful
p.fragment If they have state, they don't expose it
section
p Use objects to achieve high level conceptual programming.
p.fragment.replaceafter Law of demeter
p.fragment Principle of Least Surprise
section
p Don't do things to my data, ask me to do things.
section
p Law of demeter:
p.fragment when one wants a dog to walk,
p.fragment one does not command the dog's legs to walk directly;
p.fragment instead one commands the dog which then commands its own legs.
section
p A method may only call things in direct scope
section
p So let's say this circle represents an object with a method
img(src='resources/lod_1.png', height='300', class='noborder')
section
p It can call a method of it's own class
img(src='resources/lod_2.png', height='300', class='noborder')
+codeexample
| this.draw()
section
p Or methods of objects injected in
img(src='resources/lod_4.png', height='300', class='noborder')
+codeexample
| graphics.draw()
section
p Or methods of objects it made
img(src='resources/lod_5.png', height='300', class='noborder')
+codeexample
| local = new Gif()
| local.draw()
section
p Or methods of objects in it's class
img(src='resources/lod_3.png', height='300', class='noborder')
+codeexample
| this.helper.draw()
section
p If you can do it anyway, why am I doing it for you?
section
p Law of demeter is about #[em behavior] of objects
section
p A lot of data is hierarchical
+codeexample
| "person": [
| "head": [ "hair": "brown", "eyes": "blue" ],
| "legs": 2]
section
p Can be encoded as objects
+codeexample
| person = new Person(
| new Head(new Hair("brown"), new Eyes("blue")),
| new Legs(2), ...)
section
p Does this violate law of demeter?
+codeexample
| person.hair.color
section
p Here, person is just immutable data-structure
section
p Not the same "badness"
section
+codeexample
| public class PersonPainter implements Drawable {
| public PersonPainter(Person p)
+notes.
One benefit of immutable abstractions is it makes
parameter lists simpler
section
+codeexample
| public class PersonPainter implements Drawable {
| public PersonPainter(Hair h, Legs l, ...)
+notes.
Could be passing in each individual component.
section
p violating the law
+codeexample
| public class View {
| public void paint() {
+codeblock
| if (personPainter.getPerson()
| .hair().equals("brown") {
| // Vary paint style
| }
+notes.
What happens when a higher level class reaches into
a bheavior object's inner state?
section
p With Inheritance: Easy to add new classes
+codeexample
| public class PersonPainter implements Drawable {
| public void draw(Graphics g) {
| ...
| }
section
+codeexample
| public class DogPainter implements Drawable {
| public void draw(Graphics g) {
| ...
| }
section
p New classes for same behaviors through extends
div(style="background:#fff;")
img(src='resources/new_class.gif', height='150', class='noborder')
section
p Let's say you want to add a behavior
+codeexample
|
| public void drawOutline(Graphics g) {
|
section
+codeexample
| interface Drawable {
| public void draw(Graphics g);
+codeblock
| public void drawOutline(Graphics g);
| }
section
+codeexample
| public class PersonPainter implements Drawable {
| public void draw(Graphics g) {
| ...
| }
|
+codeblock
|
| public void drawOutline(Graphics g) {
| ...
section
div(style="background:#fff;")
img(src='resources/new_function.gif', height='150', class='noborder')
section
p Could pull "Painting" behavior into single class:
+codeexample
| public class Painter {
|
| public void draw(Object o) {
| if (o instanceof Person) {
| ...
| }
| ...
| }
section
+codeexample
| public class Painter {
|
| public void draw(Object o) {
| if (o instanceof Person) {
| ...
| }
| }
+codeblock
| public void drawOutline(Object o) {
| if (o instanceof Person) {
| ...
| }
| ...
| }
section
p But then, the only way to extend types is to add to every behavior
+codeexample
| public class Painter {
|
| public drawOutline(Object o) {
| if (o instanceof Person) {
| ...
| }
|
| if (o instanceof Dog) {
| ...
| }
+codeblock
| if (o instanceof Cat) {
| ...
| }
| ...
section
div(style="background:#fff;")
img(src='resources/new_class.gif', height='300', class='noborder')
section
p this is known as the `expression problem`
section
p There are solutions
div.fragment(style="background:#fff;")
p Clojure Protocols
img(src='resources/protocols.gif', height='300', class='noborder')
section
p Keep data safe: Don't conflate "Person" with "Painting a Person"
p One is data, the other is behavior
section
p Person was just data, wouldn't ask person to paint itself
section
p In Java, must make the tradeoff between:
ul
li.fragment easy type extension
li.fragment easy behavior extension
section
p AVOID mixing concepts:
+codeexample
| public class PersonPainter implements Drawable {
| public void draw(Graphics g) {
| ...
| }
|
| public Color getHair() {
section
p So that's Object Orientation
section
p What leads to High Cohesion?
section
p Using SOLID
p
em Martin Fowler
ul.fragment
li.fragment.dimafter Single Responsibility
li.fragment.dimafter Open/Closed
li.fragment.dimafter Liskov Substitution
li.fragment.dimafter Interface Segregation
li.fragment Dependency Inversion
+notes.
Martin fowler, prominent OO proponent
the solid princples are
section
p not the only advocate
section
p unextensible code is unmockable
+notes.
you see, it's hard to write tests if your code is unextensible
section
p Google Testability Team Testable Software Principles
p
em Jonathan Wolter, Russ Ruffer, Miško Hevery
ul.fragment
li.fragment.dimafter Class does too much
li.fragment.dimafter Reaching into collaborators
li.fragment.dimafter Has Singletons
li.fragment Constructors do real work
+notes.
section
h2 Holub on Patterns
p
em Allen Holub
ul.fragment
li.fragment.dimafter Extends is evil
li.fragment.dimafter Getters and Setters are evil
p
img.fragment(src='resources/emperor_palpatine.gif', height='300', class='noborder')
+notes.
Allen holub promonent JavaWorld, Dr. Dobbs
Consultant
section
p good code
+notes.
All these share characteristics, some are
guidelines to avoid, some are guidelines to do
section
p examples
+notes.
I am going to argue there are 2 core Principles
by which all others can be derived, let's understand
what makes code inflexible through example.
section
+codeexample
p
| class R2D3 {
| public visualize() {
| ...
| }
| }
+notes.
Let's talk about R2D3.
We said it visualizes resources using d3
a javascript framework
section
+codeexample
p
| class R2D3 {
|
+codeblock
| public visualize(String projectRoot) {
| List<File> resources = getResources(projectRoot)
| // Visualizer code
| }
|
+codeblock
| private List<File> getResources(String projectRoot) {
| ...
| }
| }
div.fragment
p
em multiple-responsibility
+notes
p so this visualize task
p calls this getResources method
section
p getResources is irreplacable, but irrelevant
section
p Unmockable.
section
p Testing requires knowledge of internals :(
+codeexample
|
| PowerMock.createPartialMock(R2D3.class,
| "getResources").andReturn(files);
|
+notes.
To change the behavior in unit test, you'd have to mock this get resources
method.
section
p Powermock requires looking at internals
img(src='resources/c3po_naked.jpg', height='400', class='noborder')
+notes.
a hidden collaborator
section
p No Mocks in production
img(src='resources/mock_programmer.gif', height='450', class='noborder')
+notes.
not like we can use them to replace behaviors.
section
p Implies an iceberg
img(src='resources/iceberg.jpg', height='400', class='noborder')
+notes.
The need to powermock implies a tight coupling on internal behavior, not
a single responsibility
Not taking advantage of OO facilities to separate behaviors
section
p API exists, why not make it explicit?
section
p what if I extend the thing
+codeexample
+codeblock
| class R2D3 extends FileFinder {
|
| public visualize(String projectRoot) {
+codeblock
| List<File> resources = getResources(projectRoot)
| // Visualizer code
| }
| }
div.fragment
p
em multiple-responsibility through
p
em extends
+notes.
the problem is this method is tightly coupled
to this class's implementation of get resources
section
+codeexample
|
| PowerMock.createPartialMock(FileFinder.class,
| "getResources").andReturn(files);
|
+notes.
Still need to mock out the behavior in test, and
if that internal class references something mock-worthy, but it, itself, isnt,
then you get into tree-hierarchy searches that are complicated
section
p Hard to Swap
img(src='resources/swap_board.gif', height='500', class='noborder')
+notes.
hard to change behaviors, hard to swap out
section
p ok i'll do it earlier
+codeexample
p
| class R2D3 {
| List<File> files;
|
+codeblock
| R2d3(String projectRoot) {
| ResourceService finder =
| new FileFinder(projectRoot)
| files = finder.getResources()
| }
|
| public visualize() {
| ...
| }
| }
div.fragment
p
em multiple-responsibility through
p
em constructor coupling
+notes
p OK, we can extract FileFinder out
p create it ahead of time in our constructor
section
p Testing requires knowledge of internals :(
+codeexample
|
| expectNew(FileFinder.class, "projectRoot")
| .andReturn(files);
|
+notes.
Now, in order to change the behavior you have to mock out internals.
section
h2 avoid new in constructors
p.fragment dependency inversion
section
p ok, no new in my constructor!!!1
+codeexample
p
| class R2D3 {
| List<File> files;
|
| R2d3(String projectRoot) {
+codeblock
| files = FileFinder.getResources(projectRoot)
| }
| }
div.fragment
p
em multiple-responsibility through
p
em Singleton / global coupling
+notes.
OK
section
p Testing requires knowledge of internals :(
+codeexample
|
| PowerMock.mockStaticPartial(
| FileFinder.class, "getResources");
| EasyMock.expect(FileFinder.getResources(...))
| .andReturn(files);
|
+notes.
Still mocking internals
section
p When do statics improve code?
p.fragment.dimafter no this
p.fragment.dimafter pure functions
p.fragment.dimafter Still is coupling
+notes.
Avoid bleeding local scope. Handy for pure functions, good no intent to override
section
+codeexample
| class R2D4 extends R2D3 {
|
| @override
| public List<File> getResources()
+notes.
One of the nice things about statics is they prevent inheritance
from being used to replace collaborator behaviors
section
p perfect code
+codeexample
| class R2D3 {
| final List<File> files;
|
+codeblock
| R2d3(List<File> visualizableFiles) {
| this.files = visualizableFiles;
| }
|
+codeblock
| public visualize() {
| // visualization code
| }
| }
+notes.
A good pattern is to separate
out the concerns and JUST pass in the files
you might also inject it into the
visualize method depending on how it works
section
p perfect is the enemy of good
+codeexample
p
| class R2D3 {
| List<File> files;
|
+codeblock
| public static R2D3 create(String projectRoot) {
| ResourceService finder =
| new FileFinder(projectRoot)
| files = finder.getResources()
| return new R2D3(files)
| }
|
+codeblock
| R2d3(List<File> visualizableFiles) {
| this.files = visualizableFiles;
| }
| ...
| }
+notes.
You can make incremental refactorings that
get you closer to ideal. Better than passing
factories everywhere
section
p so that's 'single responsibility'
section
p Ci3po: the translator
+codeexample
| class Ci3po {
| ...
| public translate() {
| ...
| }
| }
section
+codeexample
| class Ci3po {
| ...
| public translate(TaskContainer t) {
+codeblock
| List<File> files = t.getByPath(:app:fileFinder").fileFinder.getResources();
| ...
| }
| }
div.fragment
p
em tightly coupled inputs
+notes.
This is tightly coupled, to mock it:
section
p Annoying to mock.
section
+codeexample
| Ci3po.translate(new TaskContainer() {
+codeblock
| public getByPath(String s) {
| return resourceService
| }
+codeblock
| ... (40 more lines of code)
| })
+notes.
If we want to mock just the internal service, we have to
mock the full api, including the 40-some odd methods that are unused
section
p Could have been easier.
section
+codeexample
| Ci3po.translate(resourceService.getResources())
section
p Cognitive load
img(src='resources/confused_1.gif', height='500', class='noborder')
+notes.
The cognitive load to understand mirrors the actual API usage
section
+codeexample
| interface ResourceService
| public List<File> getResources()
p vs
div.fragment
+codeexample
| interface ResourceService
| public List<File> getResources()
| public void delete()
| public File find(String path)
+notes.
if a class doesn't need elements of an interface
let's not make it more complex because of them
section
p Inevitably:
+codeexample
| class YourResourceService extends ResourceService
| public delete() {
| throw new UnsupportedOperationException("nooo!")
| }
| ...
div.fragment
p
em tight coupled inputs through
p
em extreme liskov substitution
section
+codeexample
| interface ResourceService
| public List<File> getResources()
div.fragment
+codeexample
| class FileFinder
| public List<File> getResources() {
| ...
+codeblock
| public void deleteFiles() {
| ...
+notes
p Let's assume you've got a interface, resource service
p and a class that implements it
p maybe that class has a lot of other methods
p that are weird
section
+codeexample
| class Ci3po {
| ...
+codeblock
| public translate(FileFinder f) {
| List<File> = f.getResources()
| ...
| }
| }
div.fragment
p
em tightly coupled inputs through
p
em overly specific implementation
+notes.
Now I'm locked into creating a FileFinder,
if it has a lot of extra scope, I don't
want to have to build that state.
Let me pass in what you'll use!
section
p I would have to extend a real implementation.
+codeexample
| ci3po.translate(new FileFinder() {...});
section
+codeexample
| class Ci3po {
| ...
| public translate(ResourceService f) {
+codeblock
| if (f instanceof FileFinder) {
| List<File> files = (FileFinder) f.getResources()
| ...
| }
| }
| }
div.fragment
p
em tightly coupled inputs through
p
em closed for extension code
+notes.
I'm STILL locked into creating a FileFinder,
because you are using instanceof
section
h2 instanceof only on datatypes
p.fragment further reading: scala case classes
+notes.
because: if its not immutable data, can ask it to solve problem
for you.
section
p Ci3po: Translations as a service
+codeexample
| class Ci3po {
|
+codeblock
| Uploader uploader;
|
| public translate(ResourceService f) {
| ...
| }
| }
+notes.
Ok, now let's say Ci3po has an uploader it uses
section
+codeexample
| class Ci3po {
|
| Uploader uploader;
|
| public translate(ResourceService f) {
+codeblock
| uploader.setEndpoint('http://ci/translations')
| uploader.sendGet(
| new HttpGet('hasTranslations',
| params(f.getFiles()))
| ...
| }
| }
div.fragment
p
em tightly coupled inputs through
p
em reaching into state
+notes.
Translator calls into an uploader, instead of asking it
if it has translations, it makes a request, coupling to httpclient
section
p better
+codeexample
| uploader.hasTranslations(files)
section
h2 Core Principles
ol
li.fragment Classes should have a single responsibility
li.fragment Classes should not have visibility into scope they don't need.
section
p Real world applications
section
p per-ide onboarding instructions
div(style="background:#777;")
img(src='resources/refactoring_1_needs.png', class='noborder')
+notes.
We used to build the IDE onboarding instructions per IDE.
Every time a new dev needed to add onboarding instructions, they had
to change intellij and eclipse
The problem was, there was a difference between the data "what needed to be onboarded",
and the process of onboarding
section
p Refactored
div(style="background:#777;")
img(src='resources/refactoring_1.png', class='noborder')
+notes.
We pulled the IDE onboarding instructions out, they were just data
now each IDE responds to data independently
section
p view-owning-controller
div(style="background:#777;")
img(src='resources/refactoring_2.png', class='noborder')
+notes.
Another example: our view owned our controller, any time we wanted
to change navigation, we were manipulating classes that were constructing jlabels
When we need the ability for people to jump into a context for migration,
the code for this got really gross, so we made each view dumb.
Views only respond to and generate events and show sub-views. They don't transition
to new states. This is heirarchical, so subviews are dumb, but the higher level view
might have some controller logic
section
p dumb-views
div(style="background:#777;")
img(src='resources/refactoring_2_needs.png', class='noborder')
section
p Conclusions
ul
li.fragment "object oriented"
li.fragment cohesion -- best practices
section
p thank you
img(src='resources/c3po_r2d2_2.gif', height='400', class='noborder')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.