Core JavaScript

Flyweight Design Pattern

This Javascript program demonstrates a flyweight design pattern.

Flyweight.html

<!DOCTYPE html>
<html>
	<head>
		<title>XoaX.net's Javascript</title>
		<script type="text/javascript" src="Flyweight.js"></script>
	</head>
	<body onload="Initialize()">
		<canvas id="idCanvas" width="600" height ="600"
			style="background-color:#F0F0F0;border:1px lightblue solid;"></canvas>
	</body>
</html>

Flyweight.js

function Initialize() {
	const qCanvas = document.getElementById("idCanvas");
	const qContext2D = qCanvas.getContext("2d");
	
	let qFlyWeightFactory = new CFlyWeightFactory(qContext2D);
	
	let qClient = new CClient(qFlyWeightFactory, qContext2D);
	qClient.AddSquare(10, 20);
	qClient.AddSquare(480, 60);
	qClient.AddSquare(360, 450);
	qClient.AddSquare(460, 300);
	qClient.AddCircle(430, 170);
	qClient.AddCircle(240, 180);
	qClient.AddCircle(120, 340);
	qClient.Draw();
}

class CClient {
	#mqaFlyWeightIntrinsic;
	#mqaSquareExtrinsic;
	#mqaCircleExtrinsic;
	constructor(qFlyWeightFactory) {
		this.#mqaFlyWeightIntrinsic = [];
		this.#mqaFlyWeightIntrinsic["square"] = qFlyWeightFactory.GetFlyWeight("square");
		this.#mqaFlyWeightIntrinsic["circle"] = qFlyWeightFactory.GetFlyWeight("circle");
		this.#mqaSquareExtrinsic = [];
		this.#mqaCircleExtrinsic = [];
	}
	AddSquare(dX, dY) {
		this.#mqaSquareExtrinsic.push({x:dX, y:dY});
	}
	AddCircle(dX, dY) {
		this.#mqaCircleExtrinsic.push({x:dX, y:dY});
	}
	Draw(){
		for (let i = 0; i < this.#mqaSquareExtrinsic.length; ++i) {
			this.#mqaFlyWeightIntrinsic["square"].Draw(this.#mqaSquareExtrinsic[i]);
		}
		for (let i = 0; i < this.#mqaCircleExtrinsic.length; ++i) {
			this.#mqaFlyWeightIntrinsic["circle"].Draw(this.#mqaCircleExtrinsic[i]);
		}
	}
}

class CFlyWeightFactory {
	#mqaImages;
	#mqContext;
	constructor(qContext) {
		this.#mqaImages = [];
		this.#mqContext = qContext;
	}
	GetFlyWeight(sKey) {
		if (!this.#mqaImages.includes(sKey)) {
			switch(sKey) {
				case "square":
					let qSquareBitmapPromise = CreateSquare(this.#mqContext);
					this.#mqaImages[sKey] = new CConcreteSquare(qSquareBitmapPromise, this.#mqContext);
				break;
				case "circle":
					let qCircleBitmapPromise = CreateCircle(this.#mqContext);
					this.#mqaImages[sKey] = new CConcreteCircle(qCircleBitmapPromise, this.#mqContext);
				break;
				default:
					console.log("Error: Unknown key");
					return null;
			}
		}
		return this.#mqaImages[sKey];
	}
}

function CreateCircle(qContext) {
	let qImageData = qContext.createImageData(100, 100);
	for (var i = 0; i < qImageData.data.length; i += 4) {
		let x = (i % 400)/4 - 50;
		let y = Math.floor(i/400) - 50;
		let d = 49 - Math.sqrt(x*x + y*y);
		// Between -1 and 1, do a partial fill 
		qImageData.data[i] = qImageData.data[i+1] = qImageData.data[i+2] = 128;
		if (d > 1) {
			qImageData.data[i+3] = 255; // Half transparency
		} else if (d < -1){
			qImageData.data[i+3] = 0; // Totally transparent
		} else {
			qImageData.data[i+3] = Math.floor(255*(d + 1)/2);
		}
	}
	return createImageBitmap(qImageData);
}

function CreateSquare(qContext) {
	let qImageData = qContext.createImageData(100, 100);
	for (var i = 0; i < qImageData.data.length; i += 4) {
		qImageData.data[i] = qImageData.data[i+1] = qImageData.data[i+2] = 200;
		qImageData.data[i+3] = 255;
	}
	return createImageBitmap(qImageData);
}

class CAFlyWeight {
	constructor() {
		// This can be used to detect instantiation of the abstract class.
		if (this.constructor === CAFlyWeight) {
			throw new Error("Instantiated abstract class error: CAFlyWeight");
		}
	}
	Draw(qExtrinsicState) {}
}

class CConcreteSquare extends CAFlyWeight {
	#mqBitmapPromise;
	#mqContext;
	constructor(qBimapPromise, qContext) {
		super();
		this.#mqBitmapPromise = qBimapPromise;
		this.#mqContext = qContext;
	}
	Draw(qExtrinsicState) {
		// Wait for the promise to complete before rendering the image to the canvas
		this.#mqBitmapPromise.then((qBitmapResult) => {
			this.#mqContext.drawImage(qBitmapResult, qExtrinsicState.x, qExtrinsicState.y);
		});
	}
}

class CConcreteCircle extends CAFlyWeight {
	#mqBitmapPromise;
	#mqContext;
	constructor(qBimapPromise, qContext) {
		super();
		this.#mqBitmapPromise = qBimapPromise;
		this.#mqContext = qContext;
	}
	Draw(qExtrinsicState) {
		// Wait for the promise to complete before rendering the image to the canvas
		this.#mqBitmapPromise.then((qBitmapResult) => {
			this.#mqContext.drawImage(qBitmapResult, qExtrinsicState.x, qExtrinsicState.y);
		});
	}
}
 

Output

 
 

© 2007–2026 XoaX.net LLC. All rights reserved.