Flyweight pattern은 동일하거나 유사한 객체들 사이에 가능한 많은 데이터를 서로 공유하여 사용하도록 하여 메모리 사용량을 최소화하는 소프트웨어 디자인 패턴이다. 종종 오브젝트의 일부 상태 정보는 공유될 수 있는데, flyweight pattern에서는 이와 같은 상태 정보를 외부 자료 구조에 저장하여 플라이웨이트 오브젝트가 잠깐 동안 사용할 수 있도록 전달한다.
객체를 생성하고 관리하는 역할을 한다. 클라이언트가 요청할 때, 팩토리는 이미 생성된 객체를 제공하거나 새로운 객체를 생성한다.
모든 Concrete Flyweight가 구현해야 하는 인터페이스이다. 이 인터페이스는 내부 상태와 외부 상태를 기반으로 동작을 정의한다.
Flyweight 인터페이스를 구현하는 클래스로, 내부 상태를 가진다.
필요한 경우 Flyweight 인터페이스를 구현하는 클래스로, 모든 상태가 외부적인 클래스이다.
import UIKit
// flyweight
protocol Flyweight {
var sharedState: String { get }
}
// concrete flyweight
class ConcreteFlyweight: Flyweight {
var sharedState: String
init(sharedState: String) {
self.sharedState = sharedState
}
}
// flyweight factory
class FlyweightFactory {
private var flyweights: [String: Flyweight] = [:]
init(states: [String]) {
states.forEach { state in
flyweights[state] = ConcreteFlyweight(sharedState: state)
}
}
func getFlyweight(for state: String) -> Flyweight {
if let flyweight = flyweights[state] {
return flyweight
} else {
let flyweight = ConcreteFlyweight(sharedState: state)
flyweights[state] = flyweight
return flyweight
}
}
}
let factory = FlyweightFactory(states: ["1","2","3"])
let flyweight = factory.getFlyweight(for: "2")
import UIKit
// Flyweight
protocol TextStyle {
var font: UIFont { get }
var color: UIColor { get }
}
// ConcreteFlyweight
class ConcreteTextStyle: TextStyle {
var font: UIFont
var color: UIColor
init(font: UIFont, color: UIColor) {
self.font = font
self.color = color
}
}
// FlyweightFactory
class TextStyleFactory {
private var textStyles: [String: TextStyle] = [:]
func getTextStyle(for name: String) -> TextStyle {
if let textStyle = textStyles[name] {
return textStyle
} else {
let newStyle = TextStyle(font: .systemFont(ofSize: 12), color: .black)
styles[name] = newStyle
return newStyle
}
}
}
import UIKit
class ImageCache {
private var imageCache = NSCache<NSString, UIImage>()
func getImage(url: String, completion: @escaping (UIImage?) -> Void) {
if let cachedImage = imageCache.object(forKey: url as NSString) {
completion(cachedImage)
} else {
DispatchQueue.global().async {
if let url = URL(string: url),
let data = try? Data(contentsOf: url),
let image = UIImage(data: data) {
self.imageCache.setObject(image, forKey: url.absoluteString as NSString)
DispatchQueue.main.async {
completion(image)
}
} else {
DispatchQueue.main.async {
completion(nil)
}
}
}
}
}
}
let imageCache = ImageCache()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let imageUrl = "https://example.com/image.jpg"
imageCache.getImage(url: imageUrl) { image in
cell.imageView?.image = image
}
return cell
}
ImageCache
클래스를 싱글톤으로 구현하고, 이미지를 캐시하는 메서드를 제공한다. 이 클래스는 Flyweight 패턴의 Factory 역할을 담당한다.class ImageCache {
static let shared = IamgeCache()
private init() { }
private let cache = NSCache<NSString, UIImage>()
func getImage(for url: URL) -> UIImage? {
return cache.object(forKey: url.absoluteString as NSString)
}
func setImage(_ image: UIImage, for url: URL) {
cache.setObject(image, forKey: url.absoluteString as NSString)
}
}
class ImageDownloader {
func downloadImage(from url: URL, completion: @escaping (UIImage?) -> Void) {
// 이미지가 캐시에 있는지 확인
if let cachedImage = ImageCache.shared.getImage(for: url) {
completion(cachedImage)
return
}
// 이미지가 캐시에 없으면 다운로드
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, let image = UIImage(data: data) else {
completion(nil)
return
}
// 다운로드 된 이미지를 캐시에 저장
ImageCache.shared.setImage(image, for: url)
// 다운로드된 이미지를 반환
completion(image)
}
task.resume()
}
}
extension UIImageView {
func setImage(from url: URL) {
ImageDownloader().downloadImage(from: url) { [weak self] image in
DispatchQueue.main.async {
self?.image = image
}
}
}
}
class MyTableViewController: UITableViewController {
var imageURLs: [URL] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(MyCustomCell.self, forCellReuseIdentifier: "MyCustomCell")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return imageURLs.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCustomCell", for: indexPath) as! MyCustomCell
let url = imageURLs[indexPath.row]
cell.setImage(from: url)
return cell
}
}
제가 학습한 내용을 요약하여 정리한 것입니다. 내용에 오류가 있을 수 있으며, 어떠한 피드백도 감사히 받겠습니다.
감사합니다.