1 of 54

Filipe Ximenes (@xima) Partner/Developer

Pluggable Libs Through

Design Patterns

PyGotham 2018

2 of 54

  • 7 years web development
  • Django/REST APIs
  • React

Filipe Ximenes

Developer/Partner at Vinta Software

@xima

/filipeximenes

ximenes@vinta.com.br

3 of 54

We're a team of experts from Brazil.

We help our clients evolve their products the right way

with top notch development and UX techniques.

Get to know us: vintasoftware.com

4 of 54

  • vintasoftware.com/blog
  • vintasoftware.com/playbook
  • github.com/vintasoftware/django-react-boilerplate
  • github.com/vintasoftware/tapioca-wrapper

Community / Open Source

5 of 54

A Pattern Language

Christopher Alexander

6 of 54

An elegant format to refer to and debate

Together patterns should form a language that produces coherent objects

Improve people's lives

7 of 54

8 of 54

Pattern 120 - PATHS AND GOALS

The layout of paths will seem right and comfortable only when it is compatible with the process of walking. And the process of walking is far more subtle than one might imagine.

9 of 54

10 of 54

11 of 54

12 of 54

13 of 54

14 of 54

15 of 54

16 of 54

Patterns

17 of 54

"This idea […] comes simply from the observation that most of the wonderful places of the world were not made by architects but

by the people"

- Christopher Alexander

18 of 54

Language

19 of 54

Pattern 121 - PATH SHAPE

Streets should be for staying in, and not just for moving through, the way they are today.

20 of 54

Pattern 241 - SEAT SPOTS

Where outdoor seats are set down without regard for view and climate, they will almost certainly be useless.

21 of 54

Given a language, different people should be able to produce

coherent objects

22 of 54

Design Patterns

23 of 54

Design Patterns

Gang of Four (GoF)

24 of 54

The Adapter Pattern

25 of 54

Docs

# `class FacebookConsumer(token)`

- `post_new_message(text)`

- `get_latest_timeline_post()`

# `class TwitterConsumer(token)`

- `tweet_message(message)`

- `get_latest_tweet()`

26 of 54

��store_timeline_post()store_tweet()

Fetching posts from social networds

def store_timeline_post():fb_consumer = FacebookConsumer('some-token')post = fb_consumer.get_latest_timeline_post()�� database.add_post(post)

def store_tweet(twitter_consumer):tw_consumer = TwitterConsumer('some-token')tweet = tw_consumer.get_latest_tweet()�� database.add_post(tweet)

27 of 54

def store_post(consumer):post = consumer.get_post()�� database.add_post(post)���fb_consumer = FacebookConsumer('some-token')adpt_fb_consumer = FbAdapter(fb_consumer)��store_post(adpt_fb_consumer)��twitter_consumer = TwitterConsumer('some-token')adpt_tw_consumer = TwAdapter(twitter_consumer)��store_post(adpt_tw_consumer)

Adapter V1

class FbAdapter:def __init__(self, facebook_consumer):self.consumer = facebook_consumer�� def get_post(self):return (self.consumer

.get_latest_timeline_post())���class TwAdapter:def __init__(self, twitter_consumer):self.consumer = twitter_consumer�� def get_post(self):return self.consumer.get_latest_tweet()

28 of 54

fb_consumer = FacebookConsumer('some-token')adapted_fb_consumer = PostAdapter(fb_consumer, 'get_latest_timeline_post')��store_post(adapted_fb_consumer)��tw_consumer = TwitterConsumer('some-token')adapted_tw_consumer = PostAdapter(tw_consumer, 'get_latest_tweet')��store_post(adapted_tw_consumer)

Adapter V2

class PostAdapter:def __init__(self, consumer, method_name):self.consumer = consumerself.method_name = method_name�� def get_post(self):method = getattr(

self.consumer, self.method_name)return method()��def store_post(consumer):post = consumer.get_post()database.add_post(post)

29 of 54

adapted_fb_consumer = FbAdapter('some-token')��store_post(adapted_fb_consumer)

adapted_tw_consumer = TwAdapter('some-token')��store_post(adapted_tw_consumer)

Adapter V3

class FbAdapter(FacebookConsumer):�� def get_post(self):return super().get_latest_timeline_post()���class TwAdapter(TwitterConsumer):�� def get_post(self):return super().get_latest_tweet()

30 of 54

Python Social Auth (PSA) - https://github.com/python-social-auth

31 of 54

How PSA integrates with login providers

32 of 54

The Pipeline Pattern

33 of 54

def process_videos(url):page_html = download_page(url)video_links = find_youtube_links(page_html)videos = fetch_youtube_videos(video_links)videos = convert_videos_to_mp4(videos)return save_videos_to_cloud(videos)

process_videos('some url')

Processing Videos

def download_page(url):print('Downloading Page HTML'return '<h1>Some html</h1>'��def find_youtube_links(page_html):print('Finding Youtube videos in page HTML'return ['link1', 'link2']��def fetch_youtube_videos(video_links):print('Downloading Youtube videos')return ['Video Object 1', 'Video Object 2']��def convert_videos_to_mp4(videos):print('Converting Videos to MP4')return ['MP4 Video Object 1', 'MP4 Video Object 2']

34 of 54

class VideoURLFinder(ChainOfResponsibility):def execute_step(self, page_html):return find_youtube_links(page_html)��class YoutubeVideoFetcher(ChainOfResponsibility):def execute_step(self, links):return fetch_youtube_videos(links)��class MP4VideoConverter(ChainOfResponsibility):def execute_step(self, videos):return convert_videos_to_mp4(videos)��class VideoCloudUploader(ChainOfResponsibility):def execute_step(self, videos):return save_videos_to_cloud(videos)

Chain Of Responsibility

class ChainOfResponsibility:def __init__(self):self.next_step = None�� def set_next_step(self, next_step):self.next_step = next_step�� def execute(self, value):result = self.execute_step(value)if self.next_step:return self.next_step.execute(result)return result

class PageDownloader(ChainOfResponsibility):def execute_step(self, url):return download_page(url)��

35 of 54

Chain Of Responsibility

def process_videos(url):page_downloader = PageDownloader()video_finder = VideoURLFinder()youtube_fetcher = YoutubeVideoFetcher()mp4_converter = MP4VideoConverter()cloud_uploader = VideoCloudUploader()�� page_downloader.set_next_step(video_finder)video_finder.set_next_step(youtube_fetcher)youtube_fetcher.set_next_step(mp4_converter)mp4_converter.set_next_step(cloud_uploader)�� return page_downloader.execute(url)

process_videos('some url')

36 of 54

class VideoURLFinder:def execute_step(self, page_html):return find_youtube_links(page_html)��class YoutubeVideoFetcher:def execute_step(self, links):return fetch_youtube_videos(links)��class MP4VideoConverter:def execute_step(self, videos):return convert_videos_to_mp4(videos)��class VideoCloudUploader:def execute_step(self, videos):return save_videos_to_cloud(videos)

Pipeline V1

class PipelineManager:def __init__(self):self.steps = []�� def add_step(self, next_step):self.steps.append(next_step)�� def execute(self, value):result = valuefor step in self.steps:result = step.execute_step(result)return result��class PageDownloader:def execute_step(self, url):return download_page(url)

37 of 54

Pipeline V1

def process_videos(url):pipeline = PipelineManager()�� pipeline.add_step(PageDownloader())pipeline.add_step(VideoURLFinder())pipeline.add_step(YoutubeVideoFetcher())pipeline.add_step(MP4VideoConverter())pipeline.add_step(VideoCloudUploader())�� return pipeline.execute(url)

process_videos('some url')

38 of 54

def process_videos(url):pipeline = PipelineManager(['pipeline.download_page','pipeline.find_youtube_links','pipeline.fetch_youtube_videos','pipeline.convert_videos_to_mp4','pipeline.save_videos_to_cloud',])�� return pipeline.execute(url)

process_videos('some url')

Pipeline V2

class PipelineManager:def __init__(self, step_paths):self.step_paths = step_paths�� def execute(self, value):result = valuefor step_path in self.step_paths:[module_name,

function_name] = step_path.rsplit('.', 1)

module = import_module(module_name)function = getattr(module, function_name)result = function(result)return result

39 of 54

How PSA integrates to your code

https://python-social-auth.readthedocs.io/en/latest/pipeline.html

40 of 54

The Strategy Pattern

41 of 54

package = Package('Computer', 'low')deliver_package(package)

Delivering a Package

class Package: def __init__(self, content, urgency):self.content = contentself.urgency = urgency

def deliver_package(package):if package.urgency == 'low':print('The', package.content,'is going to be delivered by Bike')print('The ETA is 20 days')elif package.urgency == 'medium':print('The', package.content,'is going to be delivered by Truck')print('The ETA is 13 days')elif package.urgency == 'high':print('The', package.content,'is going to be delivered by Plane')print('The ETA is 2 days')

42 of 54

class PlaneDelivery:def __init__(self, package):self.package = package�� def deliver(self):print('The', self.package.content,

'is going to be delivered by Plane')print('The ETA is 2 days')

Strategy V1

class BikeDelivery:def __init__(self, package):self.package = package�� def deliver(self):print('The', self.package.content,

'is going to be delivered by Bike')print('The ETA is 20 days')��class TruckDelivery:def __init__(self, package):self.package = package�� def deliver(self):print('The', self.package.content,

'is going to be delivered by Truck')print('The ETA is 13 days')

43 of 54

Strategy V1

class StrategySelector:def __init__(self, package):self.package = package�� if package.urgency == 'low':self.DeliverStrategy = BikeDeliveryif package.urgency == 'medium':self.DeliverStrategy = TruckDeliveryif package.urgency == 'high':self.DeliverStrategy = PlaneDelivery�� def deliver(self):strategy = self.DeliverStrategy(self.package)strategy.deliver()��package = Package('Computer', 'low')strategy = StrategySelector(package)strategy.deliver()

44 of 54

def get_strategy(package):if package.urgency == 'low':strategy = bike_deliverif package.urgency == 'medium':strategy = truck_deliverif package.urgency == 'high':strategy = plane_deliver�� return strategy���package = Package('Computer', 'low')deliver = get_strategy(package)deliver()

Strategy V2

def bike_deliver(package):print('The', package.content,'is going to be delivered by Bike')print('The ETA is 20 days')���def truck_deliver(package):print('The', package.content,'is going to be delivered by Truck')print('The ETA is 13 days')���def plane_deliver(package):print('The', package.content,'is going to be delivered by Plane')print('The ETA is 2 days')

45 of 54

How PSA integrates with frameworks

46 of 54

Takeaways

47 of 54

Design Patterns are not created,

they emerge

48 of 54

Design Patterns are a tool for communication between programmers through

speech and code.

49 of 54

Respect accents and native functionalities of the

programming language

50 of 54

Read source code

51 of 54

Are Design Patterns a Pattern Language?

52 of 54

An elegant format to refer to and debate

Together patterns should form a language that produces coherent objects

Improve people's lives

53 of 54

"Good architecture is about improving people's lives and most of the

time this means what feels more natural or more pleasant to us"

54 of 54

Filipe Ximenes

Patner/Developer

@xima

/filipeximenes

ximenes@vinta.com.br

Thank you!

Got any questions?

Access this talk on http://bit.ly/vinta-pygotham18