
Adaptive / Flyweight Design Pattern
Read the description of other patterns.
Designing objects at the lowest levels of the system can provide its optimal flexibility, but it is accompanied by unacceptable costs of memory and performance.
As already noted, there are a large number of software systems whose purpose is to construct complex composite objects from a large number of smaller and simpler objects. At the same time, the flexibility and versatility of such systems is achieved by providing the user with a complete set of tools and primitives. It is important to understand that primitives, in this context, are elementary objects, from which composite are subsequently constructed. Moreover, the level at which the object is considered primitive, in fact, determines the applicability and effectiveness of this system. However, it is not always possible to design a system up to the lowest levels of abstraction. The memory costs and low system performance, with a direct approach, do not allow this. Therefore, when designing such systems,
The main idea of the pattern is the difference between the internal and external state of the object. The external state is conveyed by the client using the device in some context. The internal state is stored directly in the adaptation and allows you to separate them. By separation is meant the possibility of simultaneous work of several clients with the same adaptive. Thus, there are two types of opportunists - shared and not shared. Obviously, a client working with opportunists does not make assumptions about their nature, but only uses them in a given context. Therefore, the client must be provided with a transparent mechanism for the separation of fittors.
Adaptive can be used simultaneously in different contexts. It is as if he “adapts” to the context of his use. At the same time, the client has the impression that he is working with different objects. Although, in fact, it is one and the same opportunist.
Summing up, all of the above, it is worth noting that opportunists model the essence of the subject area, the number of which is too large to represent them with real objects.
Consider the task of designing a vector editor. Moreover, to get the maximum flexibility of the editor, it is necessary to design it up to the lowest levels - to the elementary primitive - the point. Obviously, with a direct approach, in one picture there can be a huge number of points, working with which and storing them in the memory of the editor will not be easy. Therefore, we will design the editor using the “Adaptive” pattern.
Let a vector editor know how to work with a limited set of primitives - Square, Circle, Point, and Image. All these primitives are opportunists. Moreover, only the image is not a shared adaptation, due to its composite nature. It is worth noting that the image in this case is an example of the “Linker” pattern. The remaining primitives should be divided on the basis of their internal state. For a square - the sizes of the sides (height, width), for a circle - radius (radius). A point, although it is a shared adaptation, does not have an internal state, in view of the fact that all the information necessary for its rendering is transmitted by the client to the so-called context of drawing (Context).
In this example, the factory - PrimitiveFactory was used to implement the mechanism of separation of opportunists. Factory is not the only mechanism for the separation of objects. In some cases, static members of the class can be dispensed with.

It is important to understand that the applicability of this pattern is determined, first of all, by how clearly the internal and external states of the system objects are clearly identified.
Problem
Designing objects at the lowest levels of the system can provide its optimal flexibility, but it is accompanied by unacceptable costs of memory and performance.
Description
As already noted, there are a large number of software systems whose purpose is to construct complex composite objects from a large number of smaller and simpler objects. At the same time, the flexibility and versatility of such systems is achieved by providing the user with a complete set of tools and primitives. It is important to understand that primitives, in this context, are elementary objects, from which composite are subsequently constructed. Moreover, the level at which the object is considered primitive, in fact, determines the applicability and effectiveness of this system. However, it is not always possible to design a system up to the lowest levels of abstraction. The memory costs and low system performance, with a direct approach, do not allow this. Therefore, when designing such systems,
An adaptive uses separation to effectively support many small objects.
The main idea of the pattern is the difference between the internal and external state of the object. The external state is conveyed by the client using the device in some context. The internal state is stored directly in the adaptation and allows you to separate them. By separation is meant the possibility of simultaneous work of several clients with the same adaptive. Thus, there are two types of opportunists - shared and not shared. Obviously, a client working with opportunists does not make assumptions about their nature, but only uses them in a given context. Therefore, the client must be provided with a transparent mechanism for the separation of fittors.
Adaptive can be used simultaneously in different contexts. It is as if he “adapts” to the context of his use. At the same time, the client has the impression that he is working with different objects. Although, in fact, it is one and the same opportunist.
Summing up, all of the above, it is worth noting that opportunists model the essence of the subject area, the number of which is too large to represent them with real objects.
Practical task
Consider the task of designing a vector editor. Moreover, to get the maximum flexibility of the editor, it is necessary to design it up to the lowest levels - to the elementary primitive - the point. Obviously, with a direct approach, in one picture there can be a huge number of points, working with which and storing them in the memory of the editor will not be easy. Therefore, we will design the editor using the “Adaptive” pattern.
Let a vector editor know how to work with a limited set of primitives - Square, Circle, Point, and Image. All these primitives are opportunists. Moreover, only the image is not a shared adaptation, due to its composite nature. It is worth noting that the image in this case is an example of the “Linker” pattern. The remaining primitives should be divided on the basis of their internal state. For a square - the sizes of the sides (height, width), for a circle - radius (radius). A point, although it is a shared adaptation, does not have an internal state, in view of the fact that all the information necessary for its rendering is transmitted by the client to the so-called context of drawing (Context).
Class diagram
In this example, the factory - PrimitiveFactory was used to implement the mechanism of separation of opportunists. Factory is not the only mechanism for the separation of objects. In some cases, static members of the class can be dispensed with.

Java implementation
/*
* Интерфейс приспособленца
*/
public interface Primitive {
/*
* Метод отрисовки примитива с передачей заданного контекста рисования
*/
public void draw(Context context);
}
/*
* Окружнсоть - разделяемый приспособленец. Внутреннее состояние - радиус
*/
public class Circle implements Primitive {
private int radius;
public Circle(int radius) {
this.radius = radius;
}
@Override
public void draw(Context context) { }
}
/*
* Разделяемый приспособленец - Квадрат.
* Внутренее состояние - высота, ширина.
*/
public class Square implements Primitive {
private int height, width;
public Square(int height, int width) {
this.height = height;
this.width = width;
}
@Override
public void draw(Context context) { }
}
/*
* Разделяемый приспособленец - точка
*/
public class Point implements Primitive {
@Override
public void draw(Context context) { }
}
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
/*
* Не разделяемый приспособленец - изображение.
*/
public class Picture implements Primitive {
private List childrens;
public Picture(Primitive ...primitives) {
this.childrens = new LinkedList();
this.childrens.addAll(Arrays.asList(primitives));
}
@Override
public void draw(Context context) {
for (Primitive p: childrens) {
p.draw(context);
}
}
}
import java.awt.Color;
/*
* Контекст рисования, передается клиентом примитиву для отрисовки последнего
*/
public final class Context {
public final int x;
public final int y;
public final Color color;
public Context(int x, int y, Color collor) {
this.x = x;
this.y = y;
this.color = collor;
}
}
import java.util.HashMap;
import java.util.Map;
/*
* Фабрика приспособленцев.
* Реализует разделение оных на основании их внутренних состояний.
*
*/
public abstract class PrimitiveFactory {
private static Point onePoint;
private static Map circles;
private static Map squares;
static {
circles = new HashMap();
squares = new HashMap();
}
public static synchronized Picture createPicture(Primitive ... childrens) {
return new Picture(childrens);
}
public static synchronized Circle createCircle(int radius) {
if (circles.get(radius) == null) {
circles.put(radius, new Circle(radius));
}
return circles.get(radius);
}
public static synchronized Square createSquare(int height, int width) {
if (squares.get(height*10+width) == null) {
squares.put(height*10+width, new Square(height, width));
}
return squares.get(height*10+width);
}
public static synchronized Point createPoint() {
if (onePoint == null) {
onePoint = new Point();
}
return onePoint;
}
}
import java.awt.Color;
// Использование
public class Main {
public static void main(String[] args) {
Primitive[] primitives = {
PrimitiveFactory.createPoint(),
PrimitiveFactory.createCircle(10),
PrimitiveFactory.createSquare(20, 30),
PrimitiveFactory.createCircle(20),
PrimitiveFactory.createCircle(20),
PrimitiveFactory.createPoint(),
PrimitiveFactory.createSquare(20, 40),
};
Picture picture = PrimitiveFactory.createPicture(primitives);
Context context = new Context(10, 20, Color.BLUE);
picture.draw(context);
}
}
* This source code was highlighted with Source Code Highlighter.
It is important to understand that the applicability of this pattern is determined, first of all, by how clearly the internal and external states of the system objects are clearly identified.