Filipe Ximenes (@xima) Partner/Developer
Pluggable Libs Through
Design Patterns
PyGotham 2018
Filipe Ximenes
Developer/Partner at Vinta Software
@xima
/filipeximenes
ximenes@vinta.com.br
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
Community / Open Source
A Pattern Language
Christopher Alexander
An elegant format to refer to and debate
Together patterns should form a language that produces coherent objects
Improve people's lives
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.
Patterns
"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
Language
Pattern 121 - PATH SHAPE
Streets should be for staying in, and not just for moving through, the way they are today.
Pattern 241 - SEAT SPOTS
Where outdoor seats are set down without regard for view and climate, they will almost certainly be useless.
Given a language, different people should be able to produce
coherent objects
Design Patterns
Design Patterns
Gang of Four (GoF)
The Adapter Pattern
Docs
# `class FacebookConsumer(token)`
- `post_new_message(text)`
- `get_latest_timeline_post()`
# `class TwitterConsumer(token)`
- `tweet_message(message)`
- `get_latest_tweet()`
��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)�
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()
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 = consumer� self.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)
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()
Python Social Auth (PSA) - https://github.com/python-social-auth
How PSA integrates with login providers
The Pipeline Pattern
�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']
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)��
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')
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 = value� for step in self.steps:� result = step.execute_step(result)� return result��class PageDownloader:� def execute_step(self, url):� return download_page(url)
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')
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 = value� for 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
How PSA integrates to your code
https://python-social-auth.readthedocs.io/en/latest/pipeline.html
The Strategy Pattern
package = Package('Computer', 'low')�deliver_package(package)
Delivering a Package
class Package:� def __init__(self, content, urgency):� self.content = content� self.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')
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')�
Strategy V1
class StrategySelector:� def __init__(self, package):� self.package = package�� if package.urgency == 'low':� self.DeliverStrategy = BikeDelivery� if package.urgency == 'medium':� self.DeliverStrategy = TruckDelivery� if 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()
def get_strategy(package):� if package.urgency == 'low':� strategy = bike_deliver� if package.urgency == 'medium':� strategy = truck_deliver� if 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')
How PSA integrates with frameworks
Takeaways
Design Patterns are not created,
they emerge
Design Patterns are a tool for communication between programmers through
speech and code.
Respect accents and native functionalities of the
programming language
Read source code
Are Design Patterns a Pattern Language?
An elegant format to refer to and debate
Together patterns should form a language that produces coherent objects
Improve people's lives
✅
❌
❗
"Good architecture is about improving people's lives and most of the
time this means what feels more natural or more pleasant to us"
Filipe Ximenes
Patner/Developer
@xima
/filipeximenes
ximenes@vinta.com.br
Thank you!
Got any questions?
Access this talk on http://bit.ly/vinta-pygotham18