Pehelysúlyú programtervezési minta
A számítástudományban a pehelysúlyú programtervezési minta, pehelysúlyú tervezési minta, vagy pehelysúlyú minta egy programtervezési minta. A pehelysúlyú objektum egy olyan objektum, amely minimalizálja  memória használatot azzal, hogy annyi adatot oszt meg, amennyi csak lehetséges más hasonló objektumokkal. Ez a nagyszámú objektumok használatának az a módja, mikor egy egyszerű ismételt reprezentáció használna fel nem elfogadható mennyiségű memóriát. Gyakran az objektum állapotának egyes részei megoszthatók, gyakorlatilag külső adat struktúrákban tároljuk őket, és csak ideiglenesen adjuk át a pehelysúlyú objektumoknak a felhasználás során.

Egy klasszikus példa a pehelysúlyú minta használatára egy szövegszerkesztőben a karakterek grafikus reprezentációja. Kívánatos lenne, hogy egy dokumentumban minden karakter egy olyan írásjel objektum lenne, amely tartalmazza a font típusát, mértét, és más a kinézetével kapcsolatos adatot, de ez akár száz vagy ezer bájt is lehet karakterenként. E helyett minden karakterhez lenne egy referencia egy megosztott  pehelysúlyú írásjel objektum, egy fajta karakternek minden példánya ugyanarra az objektumra mutatna a dokumentumban; plusz még minden karakter elhelyezkedését (a dokumentumban vagy az oldalon) kellene tárolni belsőleg az objektumban.

Egy másik példa a string internálás.

Más szövegkörnyezetben az identikus adatstruktúrák ötletét hash consing-nak is hívják.

Története 
A Programtervezési minták: Újrafelhasználható objektumorientált szoftver elemei tankönyv szerint 
a pehelysúlyú mintát először Paul Calder és Mark Linton 1990-ben alkotta meg és vizsgálta teljeskörűen, hogy hatékonyan tudja kezelni az írásjel információkat egy WYSIWYG szövegszerkesztőben , habár hasonló technikákat már használtak más rendszerekben pl. a Weinand alkalmazás keretrendszerben (1988).

Állandóság és egyenlőség 

Azért, hogy lehetővé tegyük a pehelysúlyú objektumok biztonságos megosztást a kliensek és szálak között az  objektumoknak megváltozhatatlannak kell lenniük. A pehelysúlyú objektumok definíció szerint értékkel rendelkező objektumok (angolul value objects). Ugyanannak az értéknek a két pehelysúlyú példányát azonosnak lehet tekinteni.

Lássuk például a C#-ban a következőt (operátor override ill. overloading):

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);
    }
}

Párhuzamosság 

Külön figyelmet kell fordítani a több szálon létrejövő pehelysúlyú objektumok esetére.

Ha az értékek listája előre ismert és véges, a pehelysúlyú komponensek idő előtt példányosíthatók és elkérhetők egy többszálú konténertől a versenyhelyzet nélkül. Ebben az esetben két lehetőségünk van:
 A pehelysúlyú komponens példányosítása egyszálú, bevezetve a versenyhelyzetet és biztosítva az egy példányonkénti egy értéket.
 A párhuzamos szálaknak megengedni, hogy készítsenek számos pehelysúlyú példányt, amely megszünteti a versenyhelyzetet és engedélyez több példányt értékenként. Ez a lehetőség csak akkor életképes, ha az egyenlőség kritériuma biztosított.

C# példa 

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<string, CoffeeFlavour> _cache = new Dictionary<string, CoffeeFlavour>();

    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<string, CoffeeFlavour> _cache = new ConcurrentDictionary<string, CoffeeFlavour>();

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

Egyszerű megvalósítás 
A pehelysúlyú minta lehetővé teszi azon nagyméretű adatok megosztását, amelyek közösek minden objektumban. Más szavakkal, ha azt gondoljuk, hogy ugyanaz az adat ismétlődik minden objektumban, akkor érdemes használni ezt a mintát, egy mutatóval egy egyszerű objektumra mellyel egyszerűen helyet takarítunk meg. Jelen esetben a FlyweightPointer létrehoz egy statikus Company tagot, amely a MyObject minden példányában használható.
//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 FlyWeight Company
        {
            get
            {
                return FlyWeightPointer.Company;
            }
        }
    }

Java példa 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

// 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<String, CoffeeFlavour> flavours = new HashMap<String, CoffeeFlavour>();

  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<Order> orders = new ArrayList<Order>();
  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();    
  }
  
  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());
  }
}

Ruby példa 
# 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

Fordítás

Kapcsolódó szócikkek
 Másolás írás közben
 Memoization
 Többke

Jegyzetek 

Programtervezési minták
Java-kódpéldák
C Sharp-kódpéldák
A számítástudományban a pehelysúlyú programtervezési minta , pehelysúlyú tervezési minta , vagy pehelysúlyú minta egy programtervezési minta . A pehelysúlyú objektum egy olyan objektum , amely minimalizálja memória használatot azzal , hogy annyi adatot oszt meg , amennyi csak lehetséges más hasonló objektumokkal . Ez a nagyszámú objektumok használatának az a módja , mikor egy egyszerű ismételt reprezentáció használna fel nem elfogadható mennyiségű memóriát . Gyakran az objektum állapotának egyes részei megoszthatók , gyakorlatilag külső adat struktúrákban tároljuk őket , és csak ideiglenesen adjuk át a pehelysúlyú objektumoknak a felhasználás során . Egy klasszikus példa a pehelysúlyú minta használatára egy szövegszerkesztőben a karakterek grafikus reprezentációja . Kívánatos lenne , hogy egy dokumentumban minden karakter egy olyan írásjel objektum lenne , amely tartalmazza a font típusát , mértét , és más a kinézetével kapcsolatos adatot , de ez akár száz vagy ezer bájt is lehet karakterenként . E helyett minden karakterhez lenne egy referencia egy megosztott pehelysúlyú írásjel objektum , egy fajta karakternek minden példánya ugyanarra az objektumra mutatna a dokumentumban ; plusz még minden karakter elhelyezkedését ( a dokumentumban vagy az oldalon ) kellene tárolni belsőleg az objektumban . Egy másik példa a string internálás . Más szövegkörnyezetben az identikus adatstruktúrák ötletét hash consing-nak is hívják . Története A Programtervezési minták : Újrafelhasználható objektumorientált szoftver elemei tankönyv szerint a pehelysúlyú mintát először Paul Calder és Mark Lintonben alkotta meg és vizsgálta teljeskörűen , hogy hatékonyan tudja kezelni az írásjel információkat egy WYSIWYG szövegszerkesztőben , habár hasonló technikákat már használtak más rendszerekben pl. a Weinand alkalmazás keretrendszerben Állandóság és egyenlőség Azért , hogy lehetővé tegyük a pehelysúlyú objektumok biztonságos megosztást a kliensek és szálak között az objektumoknak megváltozhatatlannak kell lenniük . A pehelysúlyú objektumok definíció szerint értékkel rendelkező objektumok ( angolul value objects ) . Ugyanannak az értéknek a két pehelysúlyú példányát azonosnak lehet tekinteni . Lássuk például a C#-ban a következőt ( operátor override ill. overloading ) : 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 ( ) \ : } public static bool operator ==( CoffeeFlavour a , CoffeeFlavour b ) { return Equals ( a , b ) ; } public static bool operator ! =( CoffeeFlavour a , CoffeeFlavour b ) { return ! Equals ( a , b ) ; } } Párhuzamosság Külön figyelmet kell fordítani a több szálon létrejövő pehelysúlyú objektumok esetére . Ha az értékek listája előre ismert és véges , a pehelysúlyú komponensek idő előtt példányosíthatók és elkérhetők egy többszálú konténertől a versenyhelyzet nélkül . Ebben az esetben két lehetőségünk van : A pehelysúlyú komponens példányosítása egyszálú , bevezetve a versenyhelyzetet és biztosítva az egy példányonkénti egy értéket . A párhuzamos szálaknak megengedni , hogy készítsenek számos pehelysúlyú példányt , amely megszünteti a versenyhelyzetet és engedélyez több példányt értékenként . Ez a lehetőség csak akkor életképes , ha az egyenlőség kritériuma biztosított. C # példa 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<string , CoffeeFlavour> _cache = new Dictionary<string , CoffeeFlavour> ( ) ; 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<string , CoffeeFlavour> _cache = new ConcurrentDictionary<string , CoffeeFlavour> ( ) ; public CoffeeFlavour GetFlavour( string flavour ) { return _cache.GetOrAdd( flavour , flv => new CoffeeFlavour( flv ) ) ; } } Egyszerű megvalósítás A pehelysúlyú minta lehetővé teszi azon nagyméretű adatok megosztását , amelyek közösek minden objektumban . Más szavakkal , ha azt gondoljuk , hogy ugyanaz az adat ismétlődik minden objektumban , akkor érdemes használni ezt a mintát , egy mutatóval egy egyszerű objektumra mellyel egyszerűen helyet takarítunk meg . Jelen esetben a FlyweightPointer létrehoz egy statikus Company tagot , amely a MyObject minden példányában használható . //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 FlyWeight Company { get { return FlyWeightPointer.Company ; } } } Java példa import java.util.ArrayList ; import java.util.HashMap ; import java.util.List ; import java.util.Map ; / / 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<String , CoffeeFlavour> flavours = new HashMap<String , CoffeeFlavour> ( ) ; 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<Order> orders = new ArrayList<Order> ( ) ; 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 ( ) ; } String report ( ) { return " \ntotal CoffeeFlavour objects made : " + menu.totalCoffeeFlavoursMade ( ) ; } public static void main( String [ ] args ) { CoffeeShop shop = new CoffeeShop ( ) ; shop.takeOrder ( " Cappuccino " , shop.takeOrder ( " Frappe " , shop.takeOrder ( " Espresso " , shop.takeOrder ( " Frappe " , shop.takeOrder ( " Cappuccino " , shop.takeOrder ( " Frappe " , shop.takeOrder ( " Espresso " , shop.takeOrder ( " Cappuccino " , shop.takeOrder ( " Espresso " , shop.takeOrder ( " Frappe " , shop.takeOrder ( " Cappuccino " , shop.takeOrder ( " Espresso " , shop.service ( ) ; System.out.println ( shop.report ( ) ) ; } } Ruby példa # 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' , hang_lamp( 'blue' , hang_lamp( 'yellow' , hang_lamp ( 'red' , hang_lamp( 'blue' , hang_lamp( 'yellow' , hang_lamp ( 'red' , hang_lamp( 'blue' , hang_lamp( 'yellow' , hang_lamp ( 'red' , hang_lamp( 'blue' , hang_lamp( 'yellow' , hang_lamp ( 'red' , hang_lamp( 'blue' , hang_lamp( 'yellow' , hang_lamp ( 'red' , hang_lamp( 'blue' , hang_lamp( 'yellow' , hang_lamp ( 'red' , hang_lamp( 'blue' , hang_lamp( 'yellow' , puts " Made # { @lamp_factory.total_number_of_lamps_made } total lamps " end end Fordítás Kapcsolódó szócikkek Másolás írás közben Memoization Többke Jegyzetek Programtervezési minták Java-kódpéldák C Sharp-kódpéldák