Bi Fonksiyon Yazdım…

Share Button

Bugün de yazılım dünyasının kanayan başka bir yarasına deyinelim dedim. :)

Kod tekrarının sıkca yapıldığı umumiyetle de OOP olmayan projelerde aynı işi farklı parametrelerle gerçekleştirip benzer çıktılar üreten fonksiyonlar görmeniz olasıdır. Genellikle bu tarz durumlarda tepesi atan bir geliştirici refactoring yapma aşkıyla sistemdeki benzer tüm fonksiyonları birleştirip leb demeden çerez tabağı üreten fonksiyonlar geliştirir.:) Bu yaklaşımın ne kadar yanlış olduğunu anlamak için öncelikle şu yazıyı incelemenizi öneririm.

Bugün de yine birlikte gerçek hayattan bir örnek inceleyeceğiz. Elektronik ticaret şirketleri, oto servisleri veya perakende ürün/hizmet satışı yapılan yerlerde müşteriye satılan bir ürün ve bu ürünün satışı ile ilgili değişik iş modelleri vardır. Partenere göre, satış kurallarına göre, kullanılan kupona göre indirim, vergi ve ürün fiyatı değişkenlik gösterebilir ve siz bu problemi “Bir tane fonksiyon yazdım…” yaklaşaımıyla çözemezsiniz. Peki bu problemin ilacı ne ola ?

Örnek olarak inceleyeceğimiz yapıda bir elektronik ticaret sitesinin sepet hesaplamasını konu alacağız. Satış modelimiz yüzdelikli ve sabit tutarlı kupon kullanımından ibaret olacak.

Sepet hesaaplaması yaparken sırasıyla aşağıdaki işlemleri yapmamız gerekiyor.

  • Kupon tipine göre indirim tutarını hesaplamak.
  • Ürün ve sepet bazında KDV tutarı hesaplamak.
  • Sepet toplamını hesaplamak.

Strategy design pattern İlk maddeyi ele alacak olursak kullanacağımız kuponun tipine bağlı olarak indirim tutarını farklı algoritmalarla hesaplamamız gerektiğinden burada kullanabileceğimiz en doğru tasarım kalıbı strateji tasarım desenidir. Strateji tasarım deseni, belirli bir işlemin farklı algoritmalarla gerçekleştirilmesi gerektiği durumlarda kullanılan oluşumsal bir tasarım desenidir. Daha fazla bilgi için bu yazıyı inceleyebilirsiniz.

Decorator Design Pattern Örneği genel olarak ele aldığımızda ise indirim, kdv ve sepet toplamı gibi hesaplamaların ayrı ayrı yapılarak siparişi güncellemesi gerektiğini görüyoruz. Bu durumda kullanabileceğimiz en doğru tasarım deseni ise dekoratör tasarım desenidir. Dekoratör tasarım deseni, bir nesnenin özelliklerinin dinamik olarak değiştirilmesi gerektiği durumlarda kullanılır. Daha fazla bilgi için bu yazıyı inceleyebilrsiniz.

Kodlama kısmı yazıyı biraz uzattığı için sizleri sıkmamak adına bu yazıda yalnızca kupon hesaplayıcıyı inceliyor olacağız.

Öncelikle sipariş ve siparişe bağlı ürün sınıflarımızı oluşturalım.

<?php 
class Order
{
	/**
	 * @var string
	 */
	public $orderNumber;
	
	/**
	 * @var float
	 */
	public $netTotal;
	
	/**
	 * @var float
	 */
	public $totalDiscount;
	
	/**
	 * @var float
	 */
	public $totalTax;
	
	/**
	 * @var float
	 */
	public $grandTotal;

	/**
	 * @var array
	 */
	public $items = array();
       
        /**
         * @string
         */
        public $couponCode;

        /**
         * @int
         */
        public $couponType;
}

class Product
{
	/**
	 * @var string
	 */
	public $sku;
	
	/**
	 * @var float
	 */
	public $netPrice;
	
	/**
	 * @var float
	 */
	public $discountPercent;
	
	/**
	 * @var float
	 */
	public $discountAmount;
	
	/**
	 * @var float
	 */
	public $taxPercent;
	
	/**
	 * @var float
	 */
	public $taxAmount;
	
	/**
	 * @var float
	 */
	public $lineTotal;
}

Kupon hesaplayıcılarımız aynı işi farklı algoritmalarla gerçekleştiren iki farklı sınıf olacağına göre belirli bir arayüze tabi olmaları gerekir.

interface ICouponCalculator
{
	const CALCULATOR_FIXED = 1;
	const CALCULATOR_PERCENT = 2;
	
	/**
	 * @param Product $product
	 * @return float
	 */
	public function calculate(Product $product);
}

İndirim miktarı sepete dağıtılacağından indirim miktarı (oran veya sabit tutar olarak) ve toplam tutar verilerini hesaplayıcı örneğini oluştururken daha sonra kullanmak üzere saklıyoruz. Bu işlemi soyut hesaplayıcı sınıfımızın __construct() metodu içerisinde gerçekleştireceğiz.

abstract class AbstractCouponCalculator
{
	/**
	 * @var float
	 */
	protected $_discount_amount;
	
	/**
	 * @var float
	 */
	protected $_total_amount;
	
	/**
	 * @var float
	 */
	protected $_rounding_errors;
	
	/**
	 * @param float $discountAmount
	 * @param float $totalAmount
	 */
	public function __construct($discountAmount, $totalAmount)
	{
		$this->_discount_amount = $discountAmount;
		$this->_total_amount = $totalAmount;
	}
}

Ön hazırlıklarımızı tamamladığımıza göre hesaplayıcılarımızı kodlamaya başlayabiliriz. İlk olarak sabit tutarlı hesaplayıcımızı kodlayarak başlayalım.

class FixedCouponCalculator extends AbstractCouponCalculator implements ICouponCalculator
{
	/**
	 * (non-PHPdoc)
	 * @see ICouponCalculator::calculate()
	 */
	public function calculate(Product $product)
	{
		$percentage = $product->netPrice / $this->_total_amount;
		$discountAmount = $this->_rounding_errors + ($this->_discount_amount * $percentage);
                //yuvarlamadan kaynaklanan hatalari ihmal etmiyoruz.
		$this->_rounding_errors = $discountAmount - round($discountAmount);
                return $discountAmount;
	}	
}

Şimdi de oransal kupon hesaplayıcımızı kodlayalım.

class PercentCouponCalculator extends AbstractCouponCalculator implements ICouponCalculator
{
	/**
	 * (non-PHPdoc)
	 * @see ICouponCalculator::calculate()
	 */
	public function calculate(Product $product)
	{
		$discountAmount = $product->netPrice * $this->_discount_amount;
		return $discountAmount;
	}
}

Geldik hesaplama stratejimizi belirlemeye. Şimdi de kupon tipine göre hesaplama yapacak strateji sınıfımızı kodlayalım.

class CouponCalculator implements ICouponCalculator
{
	/**
	 * @var ICouponCalculator
	 */
	private $_calculator;
	
	/**
	 * @param int $calculatorType
	 * @param float $discountAmount
	 * @param float $totalAmount
	 */
	public function __construct($calculatorType, $discountAmount, $totalAmount)
	{
		switch($calculatorType) {
			case ICouponCalculator::CALCULATOR_FIXED:
				$this->_calculator = new FixedCouponCalculator($discountAmount, $totalAmount);
				break;
			case ICouponCalculator::CALCULATOR_PERCENT:
				$this->_calculator = new PercentCouponCalculator($discountAmount, $totalAmount);
				break;
		}
	}
	
	/**
	 * (non-PHPdoc)
	 * @see ICouponCalculator::calculate()
	 */
	public function calculate(Product $product)
	{
		return $this->_calculator->calculate($product);
	}
} 

Bir sonraki yazıda dekoratör tasarım desenini kullanarak sizlerle birlikte sepet hesaplayıcısını kodlayacağız.

Share Button

About İbrahim Gündüz

1983 yılında İstanbul’da doğdu. İlkokul yıllarında cobol ve basic le olan tanışması, yazılıma olan ilgisini arttırdı 2005 yılında. Uludağ Üniversitesi Teknik Bilimler Meslek Yüksek Okulu Elektronik bölümünden mezun olan Gündüz, çeşitli alanlarda faaliyet gösteren kurumlarda yazılım geliştirici olarak görev almıştır. Mesleki ilgi alanları, ölçeklenebilir sistemler, uygulama entegrasyonları ve ödeme sistemleridir. Halen Markafoni back end geliştirici olarak çalışmaktadır.

4 Comments

  • Fatih P.
    Mart 20, 2013 - 9:54 pm | Permalink

    abi selam, CouponCalculator::__constructor da $discountType demissin ama asagida $discountAmount olarak yazmissin :)

  • Mart 20, 2013 - 10:08 pm | Permalink

    Kırk yılda bir copy/paste yaptım oda blog yazısına denk geldi. Sağolasın Fatih. :)

  • Pingback: İbrahim Gündüz Kişisel Blog » Tasarım, tasmam kime ne ?

  • engin
    Mayıs 4, 2013 - 9:35 pm | Permalink

    off ya ben ne zaman bu seviyede yazılım bilgisine sahip olacam merak ediyorum. güzel anlatım teşekkürler.

  • Bir Cevap Yazın

    E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir