الگوی مگس وزن

از ویکی‌پدیا، دانشنامهٔ آزاد

در برنامه‌نویسی کامپیوتری، الگوی طراحی مگس وزن یک الگوی طراحی مهندسی نرم‌افزار است. یک شئ مگس وزن نوعی شئ است که باعث صرفه جویی در مصرف حافظه می‌شود. یک شئ مگس وزن تا جایی که ممکن هست با اشیا همنوع خود به اشتراک اطلاعات می‌پردازد. وقتی که تعداد زیادی از یک نوع شی دارای اشتراک اطلاعاتی باشند، تکرار این قسمت مشترک در هر کدام از این نمونه اشیا ،باعث مصرف زیاد از حد حافظه خواهد شد، که غیرقابل قبول است. برای حل این مشکل معمولاً قسمت مشترک را در یک داده ساختار دیگر قرار داده که این اشیا سبک وزن همگی به آن دسترسی خواهند داشت.

یک نمونهٔ کلاسیک قابل ذکر نرم‌افزارهای واژه‌پرداز می‌باشند. در این نوع نرم‌افزار برای نمایش یک متن می‌توانیم دو کار بکنیم. راه اول این است که با ازای هر کاراکتر متنی در صفحه یک شی درست کنیم که اطلاعات آن کاراکتر نظیر گلیف و مکان آن کاراکتر در صفحه را ذخیره می‌کند. در صورتی که فایل متنی ما تعداد زیادی کاراکتر داشته باشد به همان مقدار هم بایستی شئ در حافظه داشته باشیم که این از نظر محدودیت حافظه عملی نمی‌باشد. راه دوم این است که به ازای هر حرف از حروف الفبای زبان، یک شی داشته باشیم که تعداد این حروف محدود است، در نتیجه تعداد این اشیا نیز محدود است. به این اشیا مگس وزن می‌گویند. هر کاراکتر در صفحه نمایش در نرم‌افزار واژه پرداز به یکی از این اشیا مگس وزن ارجاع می‌دهد. پس در این مثال دو فقره اطلاعات را در خود نگه میدارند:

  1. مکان در فایل متنی
  2. ارجاع به یک شی مگس وزن حرف الفبایی


به این ترتیب اطلاعات مربوط به گلیف به ازای هر کاراکتر تکرار نمی‌شود و برنامه از نظر مصرف حافظه بهینه می‌شود.

تاریخچه

بنابر کتاب Gang Of Four، الگوی مگس وزن برای اولین بار توسط پائل کادلر و مارک لینتون در سال ۱۹۹۰ برای یک ویرایشگر متنی به‌طور مفصل بررسی شد، با وجود این که شبیه همین تکنیک در سال‌های قبل در سیستم‌های دیگر نیز استفاده شده بود به‌طور مثال در فریم‌ورک Weinand .

تغییر ناپذیری( Immutability ) و برابری ( Equality )

برای اینکه در بین ریسمان‌های مختلف اجرایی برنامه ناسازگاری به وجود نیاید اشیا مگس وزن بایستی در طول اجرای برنامه تغییرناپذیر باشند.

و همچنین بایستی هر دو شئ مگس وزنی که مقدار یکسانی داشته باشند از نظر برنامه‌نویسی برابر ( Equal ) محسوب شوند.

برای درک بیشتر این موضوع به مثال #C زیر توجه کنید ( به تابع Equals و GetHashCode و == و =! دقت کنید)

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

مثال در جاوا

import java.util.concurrent.CopyOnWriteArrayList;
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 CopyOnWriteArrayList<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();
      orders.remove(order);
    }
  }

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