Editing Combined Patterns New

Flyweight


Objective

Eliminate or reduce redundancy when there are a large number of objects containing identical information, while achieving a balance between flexibility and performance.

Function

Reduce redundancy in situations where large quantities of objects that have identical information.

Structure

The Flyweight pattern is stored in a factory repository. The client refrains from creating Flyweights directly and requests them from Factory. Flyweight can't stand on its own. Any attribute that makes impossible to share must be provided by the customer as long as it is done a request to the Flyweight.

The structure that meets this pattern is shown in Figure 1

Responsive image

Figure 1: UML Diagram Flyweight Pattern

Applications

The use of the Flyweight pattern is recommended when:

  • The system does not depend on the identity of the object.

  • The system requires the use of large numbers of objects; this produces high storage costs.

  • Most groups of objects can be replaced by a few objects shared.

Design Patterns Collaborators

  • The Flyweight pattern works in conjunction with Composite, to represent a hierarchical structure by means of a node graphic.

  • Both the State and Strategy standards work ideally with a view to Flyweight.

Scope of action

Applied at the object level.

Problem

The application requires instantiating the same objects, with different functionality, which is highly costly in terms of memory.

Solution

The Flyweight pattern allows the sharing of objects in such a way as to employ fine granularity without the high costs of implementation; to which each object is divided into two parts: state-independent or intrinsic, stored in the Flyweight object; and state-dependent or extrinsic, stored in the client objects that are responsible for passing on their operations to Flyweight when they are invoked.

Diagram or Implementation

Responsive image

Figure 2: UML Diagram Flyweight Pattern

Figure 2 explains the behaviour of the Flyweight pattern by means of a sequence diagram.

  • The client class asks the Factory component to create a Flyweight object.

  • The Factory class before creating the object, validates if an identical object already exists who is being solicited. If so, it returns the existing object; if not, it returns the existing object, creates the new object and caches it for later use.

  • The Flyweight object is created or taken from the cache and returned to the client.


Implementations of the flyweight pattern:

    	
    		
"""
Use sharing to support large numbers of fine-grained objects
efficiently.
"""

import abc


class FlyweightFactory:
    """
    Create and manage flyweight objects.
    Ensure that flyweights are shared properly. When a client requests a
    flyweight, the FlyweightFactory object supplies an existing instance
    or creates one, if none exists.
    """

    def __init__(self):
        self._flyweights = {}

    def get_flyweight(self, key):
        try:
            flyweight = self._flyweights[key]
        except KeyError:
            flyweight = ConcreteFlyweight()
            self._flyweights[key] = flyweight
        return flyweight


class Flyweight(metaclass=abc.ABCMeta):
    """
    Declare an interface through which flyweights can receive and act on
    extrinsic state.
    """

    def __init__(self):
        self.intrinsic_state = None

    @abc.abstractmethod
    def operation(self, extrinsic_state):
        pass


class ConcreteFlyweight(Flyweight):
    """
    Implement the Flyweight interface and add storage for intrinsic
    state, if any. A ConcreteFlyweight object must be sharable. Any
    state it stores must be intrinsic; that is, it must be independent
    of the ConcreteFlyweight object's context.
    """

    def operation(self, extrinsic_state):
        pass


def main():
    flyweight_factory = FlyweightFactory()
    concrete_flyweight = flyweight_factory.get_flyweight("key")
    concrete_flyweight.operation(None)


if __name__ == "__main__":
    main()    	
    	
    	
    		
class FlyweightBook {
    private $author;
    private $title;    
    function __construct($author_in, $title_in) {
        $this->author = $author_in;
        $this->title  = $title_in;
    }      
    function getAuthor() {
        return $this->author;
    }    
    function getTitle() {
        return $this->title;
    }
}
 
class FlyweightFactory {
    private $books = array();     
    function __construct() {
        $this->books[1] = NULL;
        $this->books[2] = NULL;
        $this->books[3] = NULL;
    }  
    function getBook($bookKey) {
        if (NULL == $this->books[$bookKey]) {
            $makeFunction = 'makeBook'.$bookKey;
            $this->books[$bookKey] = $this->$makeFunction(); 
        } 
        return $this->books[$bookKey];
    }    
    //Sort of an long way to do this, but hopefully easy to follow.  
    //How you really want to make flyweights would depend on what 
    //your application needs.  This, while a little clumsy looking,
    //does work well.
    function makeBook1() {
        $book = new FlyweightBook('Larry Truett','PHP For Cats'); 
        return $book;
    }
    function makeBook2() {
        $book = new FlyweightBook('Larry Truett','PHP For Dogs'); 
        return $book;
    }
    function makeBook3() {
        $book = new FlyweightBook('Larry Truett','PHP For Parakeets'); 
        return $book;
    }
}
 
class FlyweightBookShelf {
    private $books = array();
    function addBook($book) {
        $this->books[] = $book;
    }    
    function showBooks() {
        $return_string = NULL;
        foreach ($this->books as $book) {
            $return_string .= 'title: "'.$book->getAuthor().'  author: '.$book->getTitle();"
        };
        return $return_string;
    }
}

  writeln('BEGIN TESTING FLYWEIGHT PATTERN');
 
  $flyweightFactory = new FlyweightFactory();
  $flyweightBookShelf1 =  new FlyweightBookShelf();
  $flyweightBook1 = $flyweightFactory->getBook(1);
  $flyweightBookShelf1->addBook($flyweightBook1);
  $flyweightBook2 = $flyweightFactory->getBook(1);
  $flyweightBookShelf1->addBook($flyweightBook2);
 
  writeln('test 1 - show the two books are the same book');
  if ($flyweightBook1 === $flyweightBook2) {
     writeln('1 and 2 are the same');
  } else {
     writeln('1 and 2 are not the same');    
  }
  writeln('');

  writeln('test 2 - with one book on one self twice');
  writeln($flyweightBookShelf1->showBooks());
  writeln('');

  $flyweightBookShelf2 =  new FlyweightBookShelf(); 
  $flyweightBook1 = $flyweightFactory->getBook(2);  
  $flyweightBookShelf2->addBook($flyweightBook1);
  $flyweightBookShelf1->addBook($flyweightBook1);

  writeln('test 3 - book shelf one');
  writeln($flyweightBookShelf1->showBooks());
  writeln('');

  writeln('test 3 - book shelf two');
  writeln($flyweightBookShelf2->showBooks());
  writeln('');

  writeln('END TESTING FLYWEIGHT PATTERN');
 
  function writeln($line_in) {
    echo $line_in."
"; }
    	
    		
class FlyweightFactory {
    private static Map treeMap = new TreeMap();
    private static int sharedButtons = 0;
    private static ButtonListener listener = new ButtonListener();

    public static Button makeButton(String num) {
        Button button;
        if (treeMap.containsKey(num)) {
            // 1. Identify intrinsic state (Button label)
            // 2. Return an existing object   [The same Button cannot be added
            //    multiple times to a container, and, Buttons cannot be cloned.
            //    So - this is only simulating the sharing that the Flyweight
            //    pattern provides.]
            button = new Button(((Button) treeMap.get(num)).getLabel());
            sharedButtons++;
        } else {
            // 2. Return a new object
            button = new Button(num);
            treeMap.put(num, button);
        }
        button.addActionListener(listener);
        return button;
    }

    public static void report() {
        System.out.print("new Buttons - " + treeMap.size()
                + ", \"shared\" Buttons - " + sharedButtons + ", ");
        for (Object o : treeMap.keySet()) {
            System.out.print(o + " ");
        }
        System.out.println();
    }  }

class ButtonListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
        Button button  = (Button)e.getSource();
        Component[] buttons = button.getParent().getComponents();
        int i = 0;
        for ( ; i < buttons.length; i++) {
            if (button == buttons[i]) {
                break;
            }
        }
        // 4. A third party must compute the extrinsic state (x and y)
        //    (the Button label is intrinsic state)
        System.out.println("label-" + e.getActionCommand()
                + "  x-" + i/10   + "  y-" + i%10);
    }
}

public class FlyweightDemo {
    public static void main( String[] args ) {
        Random rn = new Random();
        Frame frame = new Frame("Flyweight Demo");
        frame.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        frame.setLayout(new GridLayout(10, 10));
        // 1. Identify shareable and non-shareable state
        //    shareable - Button label, non-shareable - Button location
        for (int i=0; i < 10; i++)
            for (int j=0; j < 10; j++)
                // 3. The client must use the Factory to request objects
                frame.add(FlyweightFactory.makeButton(
                        Integer.toString(rn.nextInt(15))));
        frame.pack();
        frame.setVisible( true );
        FlyweightFactory.report();
    }
}