{
 "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
}