{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Decorator Pattern\n",
    "1. Help reduce a profusion of subclasses by adding additional responsibilities at runtime\n",
    "1. Is a structural pattern, as it offers a new way to put the program together\n",
    "1. Adds new abilities to an object, dynamically at runtime\n",
    "1. Flexible approach to subclassing\n",
    "1. Also known as **Wrapper Pattern**\n",
    "\n",
    "**Advantage**\n",
    "1. More Flexible than static inheritance\n",
    "1. Keeps things simple\n",
    "1. No practical limits\n",
    "1. Transparent to clients\n",
    "1. A decorator has a different type - (May be problematic)\n",
    "1. Many little objects\n",
    "1. Factory and Builder can help\n",
    "1. Helps to add new functionality to existing objects\n",
    "1. Better than many subclasses\n",
    "1. Better than many properties\n",
    "\n",
    "**Decorator Pattern**\n",
    "1. Class defs\n",
    "1. Wraps class instances\n",
    "1. Run time decoration\n",
    "1. Adds functionality to instances\n",
    "1. Has very specific purpose\n",
    "\n",
    "**@ Decorator**\n",
    "1. Function defs or class definitions, not instance\n",
    "1. @ sytax\n",
    "1. Compile time\n",
    "1. Add functionality to functions and classes\n",
    "1. General purpose"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Car Dealership\n",
    "Three Models\n",
    "- Economy\n",
    "- Luxury\n",
    "- Sport\n",
    "\n",
    "Options\n",
    "- Engine - 4 or 6 Cylinders\n",
    "- Paint - White, Red or Black\n",
    "- Upholstery - Leather or Vinyl"
   ]
  },
  {
   "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 ICar(metaclass=ABCMeta):\n",
    "    @abstractproperty\n",
    "    def description(self):\n",
    "        pass\n",
    "    \n",
    "    @abstractproperty\n",
    "    def cost(self):\n",
    "        pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Economy(ICar):\n",
    "    @property\n",
    "    def description(self):\n",
    "        return 'Economy'\n",
    "    \n",
    "    @property\n",
    "    def cost(self):\n",
    "        return 12000.00"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Decorators"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class IDecorator(ICar):\n",
    "    def __init__(self, car):\n",
    "        self._car = car\n",
    "        \n",
    "    @property\n",
    "    def car(self):\n",
    "        return self._car"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class V6(IDecorator):\n",
    "    @property\n",
    "    def description(self):\n",
    "        return self.car.description + ', V6'\n",
    "    \n",
    "    @property\n",
    "    def cost(self):\n",
    "        return self.car.cost + 1200.00"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class BlackPaint(IDecorator):\n",
    "    @property\n",
    "    def description(self):\n",
    "        return self.car.description + ', Black Paint'\n",
    "    \n",
    "    @property\n",
    "    def cost(self):\n",
    "        return self.car.cost + 2000.00"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Vinyl(IDecorator):\n",
    "    @property\n",
    "    def description(self):\n",
    "        return self.car.description + ', Vinyl Interior'\n",
    "    \n",
    "    @property\n",
    "    def cost(self):\n",
    "        return self.car.cost + 4000.00"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Driver Program"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Economy: 12000.0$\n",
      "Economy, Black Paint: 14000.0$\n",
      "Economy, Black Paint, V6: 15200.0$\n",
      "Economy, Black Paint, V6, Vinyl Interior: 19200.0$\n"
     ]
    }
   ],
   "source": [
    "car = Economy()\n",
    "print(f'{car.description}: {car.cost}$')\n",
    "car = BlackPaint(car)\n",
    "print(f'{car.description}: {car.cost}$')\n",
    "car = V6(car)\n",
    "print(f'{car.description}: {car.cost}$')\n",
    "car = Vinyl(car)\n",
    "print(f'{car.description}: {car.cost}$')"
   ]
  }
 ],
 "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
}