World Library  
Flag as Inappropriate
Email this Article

Flyweight pattern

Article Id: WHEBN0000140538
Reproduction Date:

Title: Flyweight pattern  
Author: World Heritage Encyclopedia
Language: English
Subject: Design Patterns, Hash consing, Interceptor pattern, Service locator pattern, Facade pattern
Collection:
Publisher: World Heritage Encyclopedia
Publication
Date:
 

Flyweight pattern

In computer programming, flyweight is a software design pattern. A flyweight is an object that minimizes memory use by sharing as much data as possible with other similar objects; it is a way to use objects in large numbers when a simple repeated representation would use an unacceptable amount of memory. Often some parts of the object state can be shared, and it is common practice to hold them in external data structures and pass them to the flyweight objects temporarily when they are used.

A classic example usage of the flyweight pattern is the data structures for graphical representation of characters in a word processor. It might be desirable to have, for each character in a document, a glyph object containing its font outline, font metrics, and other formatting data, but this would amount to hundreds or thousands of bytes for each character. Instead, for every character there might be a reference to a flyweight glyph object shared by every instance of the same character in the document; only the position of each character (in the document and/or the page) would need to be stored internally.

Another example is string interning.

In other contexts the idea of sharing identical data structures is called hash consing.

History

According to the textbook Design Patterns: Elements of Reusable Object-Oriented Software,[1] the flyweight pattern was first coined and extensively explored by Paul Calder and Mark Linton in 1990 to efficiently handle glyph information in a WYSIWYG document editor,[2] although similar techniques were already used in other systems, e.g., an application framework by Weinand et al. (1988).[3]

Immutability and equality

To enable safe sharing, between clients and threads, Flyweight objects must be immutable. Flyweight objects are by definition value objects. The identity of the object instance is of no consequence therefore two Flyweight instances of the same value are considered equal.

Example in C# (note Equals and GetHashCode overrides as well as == and != operator overloads):


public class CoffeeFlavour {
    private readonly string _flavour;

    public CoffeeFlavour(string flavour) {
        _flavour = flavour;
    }

    public string Flavour {
        get { return _flavour; }
    }

    public override bool Equals(object obj) {
        if (ReferenceEquals(null, obj)) return false;
        return obj is CoffeeFlavour && Equals((CoffeeFlavour)obj);
    }

    public bool Equals(CoffeeFlavour other) {
        return string.Equals(_flavour, other._flavour);
    }

    public override int GetHashCode() {
        return (_flavour != null ? _flavour.GetHashCode() : 0);
    }

    public static bool operator ==(CoffeeFlavour a, CoffeeFlavour b) {
        return Equals(a, b);
    }

    public static bool operator !=(CoffeeFlavour a, CoffeeFlavour b) {
        return !Equals(a, b);
    }
}

Concurrency

Special consideration must be made in scenarios where Flyweight objects are created on multiple threads. If the list of values is finite and known in advance the Flyweights can be instantiated ahead of time and retrieved from a container on multiple threads with no contention. If Flyweights are instantiated on multiple threads there are two options:

  1. Make Flyweight instantiation single threaded thus introducing contention and ensuring one instance per value.
  2. Allow concurrent threads to create multiple Flyweight instances thus eliminating contention and allowing multiple instances per value. This option is only viable if the equality criterion is met.

Example in C#

using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;

public interface ICoffeeFlavourFactory {
    CoffeeFlavour GetFlavour(string flavour);
}

public class ReducedMemoryFootprint : ICoffeeFlavourFactory {
    private readonly object _cacheLock = new object();
    private readonly IDictionary _cache = new Dictionary();

    public CoffeeFlavour GetFlavour(string flavour) {
        if (_cache.ContainsKey(flavour)) return _cache[flavour];
        var coffeeFlavour = new CoffeeFlavour(flavour);
        ThreadPool.QueueUserWorkItem(AddFlavourToCache, coffeeFlavour);
        return coffeeFlavour;
    }

    private void AddFlavourToCache(object state) {
        var coffeeFlavour = (CoffeeFlavour)state;
        if (!_cache.ContainsKey(coffeeFlavour.Flavour)) {
            lock (_cacheLock) {
                if (!_cache.ContainsKey(coffeeFlavour.Flavour)) _cache.Add(coffeeFlavour.Flavour, coffeeFlavour);
            }
        }
    }
}

public class MinimumMemoryFootprint : ICoffeeFlavourFactory {
    private readonly ConcurrentDictionary _cache = new ConcurrentDictionary();

    public CoffeeFlavour GetFlavour(string flavour) {
        return _cache.GetOrAdd(flavour, flv => new CoffeeFlavour(flv));
    }
}

Simple implementation

Flyweight allows you to share bulky data which are common to each object. In other words, if you think that same data is repeating for every object, you can use this pattern to point to the single object and hence can easily save space. Here the FlyweightPointer creates a static member Company, which is used for every object of MyObject.

//IVSR: simple flyweight example in C#
    // Defines Flyweight object which repeats itself.
    public class FlyWeight
    {
        public string Company { get; set; }
        public string CompanyLocation { get; set; }
        public string CompanyWebSite { get; set; }
        //Bulky Data
        public byte[] CompanyLogo { get; set; } 
    }
    public static class FlyWeightPointer
    {
        public static FlyWeight Company = new FlyWeight
        {
            Company = "Abc",
            CompanyLocation = "XYZ",
            CompanyWebSite = "www.abc.com"
        };
    }
    public class MyObject
    {
        public string Name { get; set; }
        public string Company
        {
            get
            {
                return FlyWeightPointer.FlyWeight.Company;
            }
        }
    }

Example in Java

import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;

// Instances of CoffeeFlavour will be the Flyweights
class CoffeeFlavour {
  private final String name;

  CoffeeFlavour(String newFlavor) {
    this.name = newFlavor;
  }

  @Override
  public String toString() {
    return name;
  }
}

// Menu acts as a factory and cache for CoffeeFlavour flyweight objects
class Menu {
  private Map flavours = new ConcurrentHashMap();

  CoffeeFlavour lookup(String flavorName) {
    if (!flavours.containsKey(flavorName))
      flavours.put(flavorName, new CoffeeFlavour(flavorName));
    return flavours.get(flavorName);
  }

  int totalCoffeeFlavoursMade() {
    return flavours.size();
  }
}

class Order {
  private final int tableNumber;
  private final CoffeeFlavour flavour;

  Order(int tableNumber, CoffeeFlavour flavor) {
    this.tableNumber = tableNumber;
    this.flavour = flavor;
  }

  void serve() {
    System.out.println("Serving " + flavour + " to table " + tableNumber);
  }
}

public class CoffeeShop {
  private final List orders = new Vector();
  private final Menu menu = new Menu();

  void takeOrder(String flavourName, int table) {
    CoffeeFlavour flavour = menu.lookup(flavourName);
    Order order = new Order(table, flavour);
    orders.add(order);
  }
  
  void service() {
    for (Order order : orders) {
      order.serve();
      orders.remove(order);    
    }
  }
  
  String report() {
    return "\ntotal CoffeeFlavour objects made: "
        + menu.totalCoffeeFlavoursMade();
  }

  public static void main(String[] args) {
    CoffeeShop shop = new CoffeeShop();

    shop.takeOrder("Cappuccino", 2);
    shop.takeOrder("Frappe", 1);
    shop.takeOrder("Espresso", 1);
    shop.takeOrder("Frappe", 897);
    shop.takeOrder("Cappuccino", 97);
    shop.takeOrder("Frappe", 3);
    shop.takeOrder("Espresso", 3);
    shop.takeOrder("Cappuccino", 3);
    shop.takeOrder("Espresso", 96);
    shop.takeOrder("Frappe", 552);
    shop.takeOrder("Cappuccino", 121);
    shop.takeOrder("Espresso", 121);

    shop.service();
    System.out.println(shop.report());
  }
}

Example in Scala

/*
https://gist.github.com/pkinsky/111aee2f129c03ff1d0d
run as a script using `scala flyweight.scala`
expected output:
  Serving CoffeeFlavour(Espresso) to table 121
  Serving CoffeeFlavour(Cappuccino) to table 121
  Serving CoffeeFlavour(Frappe) to table 552
  Serving CoffeeFlavour(Espresso) to table 96
  Serving CoffeeFlavour(Cappuccino) to table 3
  Serving CoffeeFlavour(Espresso) to table 3
  Serving CoffeeFlavour(Frappe) to table 3
  Serving CoffeeFlavour(Cappuccino) to table 97
  Serving CoffeeFlavour(Frappe) to table 897
  Serving CoffeeFlavour(Espresso) to table 1
  Serving CoffeeFlavour(Frappe) to table 1
  Serving CoffeeFlavour(Cappuccino) to table 2
  total CoffeeFlavour objects made: 3
*/

class CoffeeFlavour(val name: String){
    override def toString = s"CoffeeFlavour($name)"
}

object CoffeeFlavour {
    import scala.collection.mutable.Map
    private val cache = Map.empty[String, CoffeeFlavour]

    def apply(name: String): CoffeeFlavour =
        cache.getOrElseUpdate(name, new CoffeeFlavour(name))

    def totalCoffeeFlavoursMade = cache.size
}


case class Order(tableNumber: Int, flavour: CoffeeFlavour){

  def serve: Unit =
    println(s"Serving $flavour to table $tableNumber")
}

object CoffeeShop {
  var orders = List.empty[Order]

  def takeOrder(flavourName: String, table: Int) {
    val flavour = CoffeeFlavour(flavourName)
    val order = Order(table, flavour)
    orders = order :: orders
  }

  def service: Unit = orders.foreach(_.serve)

  def report =
    s"total CoffeeFlavour objects made: ${CoffeeFlavour.totalCoffeeFlavoursMade}"
}


CoffeeShop.takeOrder("Cappuccino", 2)
CoffeeShop.takeOrder("Frappe", 1)
CoffeeShop.takeOrder("Espresso", 1)
CoffeeShop.takeOrder("Frappe", 897)
CoffeeShop.takeOrder("Cappuccino", 97)
CoffeeShop.takeOrder("Frappe", 3)
CoffeeShop.takeOrder("Espresso", 3)
CoffeeShop.takeOrder("Cappuccino", 3)
CoffeeShop.takeOrder("Espresso", 96)
CoffeeShop.takeOrder("Frappe", 552)
CoffeeShop.takeOrder("Cappuccino", 121)
CoffeeShop.takeOrder("Espresso", 121)

CoffeeShop.service
println(CoffeeShop.report)

Example in Ruby

# Flyweight Object
class Lamp
  attr_reader :color
  #attr_reader makes color attribute available outside 
  #of the class by calling .color on a Lamp instance

  def initialize(color)
    @color = color
  end
end

class TreeBranch
  def initialize(branch_number)
    @branch_number = branch_number
  end

  def hang(lamp)
    puts "Hang #{lamp.color} lamp on branch #{@branch_number}"
  end
end

# Flyweight Factory
class LampFactory
  def initialize
    @lamps = {}
  end

  def find_lamp(color)
    if @lamps.has_key?(color)
      # if the lamp already exists, reference it instead of creating a new one
      lamp = @lamps[color]
    else
      lamp = Lamp.new(color)
      @lamps[color] = lamp
    end
    lamp
  end

  def total_number_of_lamps_made
    @lamps.size
  end
end

class ChristmasTree
  def initialize
    @lamp_factory = LampFactory.new
    @lamps_hung = 0

    dress_up_the_tree
  end

  def hang_lamp(color, branch_number)
    TreeBranch.new(branch_number).hang(@lamp_factory.find_lamp(color))
    @lamps_hung += 1
  end

  def dress_up_the_tree
    hang_lamp('red', 1)
    hang_lamp('blue', 1)
    hang_lamp('yellow', 1)
    hang_lamp('red', 2)
    hang_lamp('blue', 2)
    hang_lamp('yellow', 2)
    hang_lamp('red', 3)
    hang_lamp('blue', 3)
    hang_lamp('yellow', 3)
    hang_lamp('red', 4)
    hang_lamp('blue', 4)
    hang_lamp('yellow', 4)
    hang_lamp('red', 5)
    hang_lamp('blue', 5)
    hang_lamp('yellow', 5)
    hang_lamp('red', 6)
    hang_lamp('blue', 6)
    hang_lamp('yellow', 6)
    hang_lamp('red', 7)
    hang_lamp('blue', 7)
    hang_lamp('yellow', 7)
    puts "Made #{@lamp_factory.total_number_of_lamps_made} total lamps"
  end
end

Example in Python

By default, instances of hash table for each instance. [4]

class Part(object):

    __slots__ = ['part_number', 'part_name', 'unit_cost']

    def __init__(self, part_number, part_name, unit_cost):
        self.part_number = part_number
        self.part_name = part_name
        self.unit_cost = unit_cost

    def total_cost(self, quantity):
        return self.unit_cost * quantity

assembly = [(30, Part('XL123', 'xwidget', 1.25)),
            (20, Part('CQ456', 'jcog', 3.75)),
            (12, Part('MT789', 'cgear', 2.50))]

print(sum(part.total_cost(quantity) for quantity, part in assembly))

Example in Swift

import Foundation

// Instances of CoffeeFlavour will be the Flyweights
class CoffeeFlavor : CustomStringConvertible {
    var flavor: String
    var description: String {
        get {
            return flavor
        }
    }
    
    init(flavor: String) {
        self.flavor = flavor
    }
}

// Menu acts as a factory and cache for CoffeeFlavour flyweight objects
class Menu {
    private var flavors: [String: CoffeeFlavor] = [:]
    
    func lookup(flavor: String) -> CoffeeFlavor {
        if flavors.indexForKey(flavor) == nil {
            flavors[flavor] = CoffeeFlavor(flavor: flavor)
        }
        return flavors[flavor]!
    }
}

class CoffeeShop {
    private var orders: [Int: CoffeeFlavor] = [:]
    private var menu = Menu()
    
    func takeOrder(flavor flavor: String, table: Int) {
        orders[table] = menu.lookup(flavor)
    }
    
    func serve() {
        for (table, flavor) in orders {
            print("Serving \(flavor) to table \(table)")
        }
    }
}

let coffeeShop = CoffeeShop()
coffeeShop.takeOrder(flavor: "Cappuccino", table: 1)
coffeeShop.takeOrder(flavor: "Frappe", table: 3);
coffeeShop.takeOrder(flavor: "Espresso", table: 2);
coffeeShop.takeOrder(flavor: "Frappe", table: 15);
coffeeShop.takeOrder(flavor: "Cappuccino", table: 10);
coffeeShop.takeOrder(flavor: "Frappe", table: 8);
coffeeShop.takeOrder(flavor: "Espresso", table: 7);
coffeeShop.takeOrder(flavor: "Cappuccino", table: 4);
coffeeShop.takeOrder(flavor: "Espresso", table: 9);
coffeeShop.takeOrder(flavor: "Frappe", table: 12);
coffeeShop.takeOrder(flavor: "Cappuccino", table: 13);
coffeeShop.takeOrder(flavor: "Espresso", table: 5);
coffeeShop.serve()

See also

References

  1. ^
  2. ^
  3. ^
  4. ^ https://docs.python.org/3/reference/datamodel.html#slots
This article was sourced from Creative Commons Attribution-ShareAlike License; additional terms may apply. World Heritage Encyclopedia content is assembled from numerous content providers, Open Access Publishing, and in compliance with The Fair Access to Science and Technology Research Act (FASTR), Wikimedia Foundation, Inc., Public Library of Science, The Encyclopedia of Life, Open Book Publishers (OBP), PubMed, U.S. National Library of Medicine, National Center for Biotechnology Information, U.S. National Library of Medicine, National Institutes of Health (NIH), U.S. Department of Health & Human Services, and USA.gov, which sources content from all federal, state, local, tribal, and territorial government publication portals (.gov, .mil, .edu). Funding for USA.gov and content contributors is made possible from the U.S. Congress, E-Government Act of 2002.
 
Crowd sourced content that is contributed to World Heritage Encyclopedia is peer reviewed and edited by our editorial staff to ensure quality scholarly research articles.
 
By using this site, you agree to the Terms of Use and Privacy Policy. World Heritage Encyclopedia™ is a registered trademark of the World Public Library Association, a non-profit organization.
 


Copyright © World Library Foundation. All rights reserved. eBooks from Project Gutenberg are sponsored by the World Library Foundation,
a 501c(4) Member's Support Non-Profit Organization, and is NOT affiliated with any governmental agency or department.