{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Adapter Pattern\n", "The purpose is to present a client code with abstract api, rather than a number of different apis with similar goals\n", "1. Favours to program abstraction rather than to concrete objects (D - Dependency Inversion)\n", "1. Open Closed principle (O)\n", "1. Used to convert interface of a class into one that client expects\n", "\n", "> **Example**\n", "> - AC Adapters\n", "> - Pipe Adapter\n", "> - US - India Power Sockets\n", "\n", "> Also Known As the Wrapper Pattern" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# A program made to Print Customer Name and Address\n", "1. Later on in project, we're asked to make it work for Vendor object\n", "1. Unfortunately, currently they are different APIs - Vendor (Street and Number diff) - Customer (Combined Address Property)\n", "1. Can't just copy and paste the Customer Name printer object, it would violate (DRY - Don't Repeat Yourself!)\n", "1. Add some conditional logic, to handle both prints - (Worthless!)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 2 Types\n", "1. Object adapter - Composition - Can easily work with both parent and child class\n", "1. Class adapter - Inheritance - It lets the Adapter override some of the classes methods\n", "\n", "> **Favour Composition over Inheritance always!**\n", "> - It leads to flatter class structure\n", "> - Easy to understand, Debug and Maintain" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Object Adapter" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from abc import ABCMeta, abstractproperty" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class Customer():\n", " def __init__(self, name, address):\n", " self._name = name\n", " self._address = address\n", " \n", " @property\n", " def name(self):\n", " return self._name\n", " \n", " @property\n", " def address(self):\n", " return self._address" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "MOCK_CUSTOMERS = (\n", " Customer('Pizza Love', '33 Peperoni Lane'),\n", " Customer('Happy and Green', '25 Kale St.'),\n", " Customer('Sweet Tooth', '42 Chocolate Ave.')\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adaptee" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class Vendor():\n", " def __init__(self, name, number, street):\n", " self._name = name\n", " self._number = number\n", " self._street = street\n", " \n", " @property\n", " def name(self):\n", " return self._name\n", " \n", " @property\n", " def number(self):\n", " return self._number\n", " \n", " @property\n", " def street(self):\n", " return self._street" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adapter" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class IAdapter(metaclass=ABCMeta):\n", " def __init__(self, adaptee):\n", " self._adaptee = adaptee\n", " \n", " @property\n", " def adaptee(self):\n", " return self._adaptee\n", " \n", " @abstractproperty\n", " def name(self):\n", " pass\n", " \n", " @abstractproperty\n", " def address(self):\n", " pass" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class VendorAdapter(IAdapter):\n", " @property\n", " def name(self):\n", " return self.adaptee.name\n", " \n", " @property\n", " def address(self):\n", " return f'{self.adaptee.number} {self.adaptee.street}'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Composition" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "MOCK_VENDORS = (\n", " VendorAdapter(Vendor('Dough Factory', 1, 'Semolina Court')),\n", " VendorAdapter(Vendor('Farm Produce', 14, 'Country Rd.')),\n", " VendorAdapter(Vendor('Cocoa World', 53, 'Tropical Blvd.'))\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Print works for both Customers and Vendors" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Name: Pizza Love, Address: 33 Peperoni Lane\n", "Name: Happy and Green, Address: 25 Kale St.\n", "Name: Sweet Tooth, Address: 42 Chocolate Ave.\n" ] } ], "source": [ "CUSTOMERS = MOCK_CUSTOMERS\n", "\n", "for cust in CUSTOMERS:\n", " print(f'Name: {cust.name}, Address: {cust.address}')" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Name: Dough Factory, Address: 1 Semolina Court\n", "Name: Farm Produce, Address: 14 Country Rd.\n", "Name: Cocoa World, Address: 53 Tropical Blvd.\n" ] } ], "source": [ "CUSTOMERS = MOCK_VENDORS\n", "\n", "for cust in CUSTOMERS:\n", " print(f'Name: {cust.name}, Address: {cust.address}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Class Adapter\n", "1. Inherits both classes, and then modifies" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": true }, "outputs": [], "source": [ "class VendorAdapter(Vendor, Customer):\n", " def __init__(self, *args, **kwargs):\n", " super().__init__(*args, **kwargs)\n", " \n", " @property\n", " def address(self):\n", " return f'{self.number} {self.street}'" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": true }, "outputs": [], "source": [ "MOCK_VENDORS = (\n", " VendorAdapter('Dough Factory', 1, 'Semolina Court'),\n", " VendorAdapter('Farm Produce', 14, 'Country Rd.'),\n", " VendorAdapter('Cocoa World', 53, 'Tropical Blvd.')\n", ")" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Name: Dough Factory, Address: 1 Semolina Court\n", "Name: Farm Produce, Address: 14 Country Rd.\n", "Name: Cocoa World, Address: 53 Tropical Blvd.\n" ] } ], "source": [ "CUSTOMERS = MOCK_VENDORS\n", "\n", "for cust in CUSTOMERS:\n", " print(f'Name: {cust.name}, Address: {cust.address}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Pros and Cons\n", "**Object Adapter**\n", "1. Composition over Inheritance\n", "1. Delegates to Adaptee\n", "1. Works with all adaptee subclasses (If they don't change) - Open Close Principle maybe broken\n", "\n", "**Class Adapter**\n", "1. Subclassing\n", "1. Overrides Adaptee's methods" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.3" } }, "nbformat": 4, "nbformat_minor": 2 }