Skip to content

Latest commit

 

History

History
531 lines (362 loc) · 10 KB

talk.rst

File metadata and controls

531 lines (362 loc) · 10 KB
title:OOD Rules
data-transition-duration:500
css:talk.css

id:title-slide

OOD Rules

OOD = Object Oriented Design

img/planet-of-teh-ood.png

Hamish Downer / hgd20 / foobacca / aptivate.org


The Rules


class:large-code

The Rules

assert len(class) <= 100

class:large-code

The Rules

assert len(class) <= 100
assert len(method) <= 5

class:large-code

The Rules

assert len(class) <= 100
assert len(method) <= 5
assert len(method.params) <= 4

img/free-the-ood.jpg


class:large-code

The Rules

assert len(class) <= 100
assert len(method) <= 5
assert len(method.params) <= 4

class Meta:
    # you must persuade your pair
    # if you want to break a rule

img/technicolour-ood.jpg


Example Time


Planning Work

img/tomsplanner-screenshot.png


JSON Export

img/tomsplanner-json-screenshot.png


JSON Export Detail

{
    "duration": 8,
    "durationtype": "day",
    "finishmoment": "09/22/2015 17:00:00 GMT",
    "id": "grid_0_33",
    "kids": [],
    "label": "Dalek",
    "nature": "period",
    "startmoment": "09/17/2015 09:00:00 GMT",
    "type": 26
},

img/calendar_data_before.png


Squint Test

img/calendar_data_before_squint.png


def get_blank_activity(self):
    return {
        'nature': None,
        'type': None,
        'verbose_type': 'empty',
    }

def add_period(self, period):
    # ...
    for halfday in xhalfdays(start_day, end_day):
        self.days[halfday] = {
            'nature': 'period',
            'type': period['type'],
            'verbose_type': self.period_legend.get(period['type']),
            'label': period.get('label'),
        }

def add_symbol(self, symbol):
    # ...
    for halfday in xhalfdays_for_day(day):
        self.days[halfday] = {
            'nature': 'symbol',
            'type': symbol['type'],
            'verbose_type': self.symbol_legend.get(symbol['type']),
            'label': self.symbol_legend.get(symbol['type']),
        }

img/ood-can-do-it.jpg


class Activity(object):

    def __init__(self, **kwargs):
        self.extra_init(**kwargs)

    def extra_init(self):
        # to be overridden by child classes
        pass

    def __unicode__(self):
        return "{} - {}/{} - {}".format(
            self.nature,
            self.activity_type,
            self.verbose_type,
            self.label
        )


class EmptyActivity(Activity):
    def extra_init(self):
        self.nature = 'empty'
        self.activity_type = 0
        self.verbose_type = 'empty'
        self.label = 'empty'

    def __unicode__(self):
        return "empty"

class PeriodActivity(Activity):
    def extra_init(self, activity_type, verbose_type, label=None):
        self.nature = 'period'
        self.activity_type = activity_type
        self.verbose_type = verbose_type
        if label:
            self.label = label
        else:
            self.label = verbose_type


class SymbolActivity(Activity):
    def extra_init(self, activity_type, verbose_type):
        self.nature = 'symbol'
        self.activity_type = activity_type
        self.verbose_type = verbose_type
        self.label = verbose_type

    def __unicode__(self):
        return "{} - {}/{}".format(
            self.nature,
            self.activity_type,
            self.verbose_type,
        )

def add_period(self, period):
    # ...
    for halfday in xhalfdays(start_day, end_day):
        self.days[halfday] = PeriodActivity(
            activity_type=period['type'],
            verbose_type=self.period_legend.get(period['type']),
            label=period.get('label')
        )

def add_symbol(self, symbol):
    # ...
    for halfday in xhalfdays_for_day(day):
        self.days[halfday] = SymbolActivity(
            activity_type=symbol['type'],
            verbose_type=self.symbol_legend.get(symbol['type'])
        )

img/calendar_data_after.png


Squint Test Before

img/calendar_data_before_squint.png


Squint Test After

img/calendar_data_after_squint.png


Inheritance

Is it a good idea?


img/in_the_ood_for_love.png


Inheritance

Shallow and Narrow

Composition instead of Inheritance


Django Class-Based Views

UpdateView ancestry


Open Closed

  • Open for extension
  • Closed for modification

super() vs extra_init()

class Activity(object):
    def __init__(self, **kwargs):
        # ...

class PeriodActivity(Activity):
    def __init__(self, **kwargs):
        super(SymbolActivity, self).__init__(**kwargs)
        # ...

super() vs extra_init()

img/wrong-parent.jpg


super() vs extra_init()

class Activity(object):
    def __init__(self, **kwargs):
        # ...
        self.extra_init(**kwargs)

    def extra_init(self, **kwargs):
        pass

class PeriodActivity(Activity):
    def extra_init(self, **kwargs):
        # ...

img/ood-tea-cosy.jpg


Example 2


def halfday_str(day, am_pm):
    return day.isoformat() + '-' + am_pm


def xhalfdays(start, end):
    """ generator: takes two datetimes, and produces strings like:
        2015-05-28-am
        2015-05-28-pm
        2015-05-29-am
        2015-05-29-pm

    Note it should skip weekends
    """
    start_am_pm = 'am' if start.hour < 12 else 'pm'
    end_am_pm = 'am' if end.hour < 14 else 'pm'
    day = start.date()
    end_day = end.date()
    one_day = timedelta(days=1)

# def xhalfdays(start, end):  # continued
    if start_am_pm == 'pm':
        if is_weekday(day):
            yield halfday_str(day, 'pm')
        day += one_day

    while day < end_day:
        if is_weekday(day):
            yield halfday_str(day, 'am')
            yield halfday_str(day, 'pm')
        day += one_day

    if day == end_day and is_weekday(day):
        yield halfday_str(day, 'am')
        if end_am_pm == 'pm':
            yield halfday_str(day, 'pm')

class HalfDay(object):
    def __init__(self, day, am_pm):
        self.day = day
        self.am_pm = am_pm

    def increment(self):
        if self.pm:
            return HalfDay(self.day + self.one_day, self.AM)
        else:
            return HalfDay(self.day, self.PM)

    def increment_weekday(self):
        halfday = self.increment()
        while not halfday.is_weekday():
            halfday = halfday.increment()
        return halfday

    def __lt__(self, other):
        return (
            self.day < other.day or (
                self.day == other.day and
                self.am and other.pm
            )
        )

    def is_weekday(self):
    def __unicode__(self):

class HalfDayRange(object):

    def __init__(self, start, end):
        self.start_half_day = self.from_start_date(start)
        self.end_half_day = self.from_end_date(end)

    def from_start_date(self, date_time):
        am_pm = 'am' if date_time.hour < 12 else 'pm'
        halfday = HalfDay(date_time.date(), am_pm)
        if not halfday.is_weekday():
            halfday = halfday.increment_weekday()
        return halfday

    def from_end_date(self, date_time):
        am_pm = 'am' if date_time.hour < 14 else 'pm'
        return HalfDay(date_time.date(), am_pm)

    def xhalfdays(self):
        half_day = self.start_half_day
        while half_day <= self.end_half_day:
            yield unicode(half_day)
            half_day = half_day.increment_weekday()

Other Pearls of Wisdom


img/make-the-change-easy.png


A little duplication is better than the wrong abstraction


Think about objects in terms of messages and message passing


img/pasta-ood.jpg


The Rules

class:large-code
assert len(class) <= 100
assert len(method) <= 5
assert len(method.params) <= 4

class Meta:
    # you must persuade your pair
    # if you want to break a rule

img/oods-in-your-favour.jpg


github.com/foobacca/ood-rules

sandimetz.com

img/poodr.jpeg

Hamish Downer / hgd20 / foobacca / aptivate.org