Tag Archives: Software Engineering

Genel Yazılım ve Sistem Mühendisliği

Süzgeç Tasarım Deseni

Belirli bir nesne grubunun bir veya daha fazla kritere göre filtrelenebilmesini sağlayan yapısal tasarım desenidir. Tanımdan da anlaşılacağı üzere filtreleme işlemi tekil veya zincir halinde gerçekleştirilebilir.

Gerçek Hayat Örneği:

Dilerseniz bu örneği Dekoratör Tasarım Deseni yazısında söz ettiğimiz e-ticaret uygulaması üzerinden devam ettirelim.

Kısaca hatırlatmak gerekirse sitemizden aynı kategoriden 3 ürün alan müşteriye 2 fiyatı ödetecek ve 10 TL indirim vereecktik. Bu işlemi yaparken dekoratör ve strateji tasarım desenlerinden yararlanmıştık. Şimdi bu örneği biraz daha özelleştirelim.

Kullanıcımızın, yeni yıl kategorisinden satın alacağı 3 adet 100 TL üzeri ürün için 2 fiyatı ödetelim ve 10 TL indirim uygulayalım.

Uygulama:

Örnekde belirttiğimiz şartlara uygun olan sepet ürünlerini filtrelemek için oluşturacağımız filtrelerin implement edeceği arayüz sınıfını oluşturalım.

<?php
namespace Promotion;

interface FilterInterface
{
    /**
     * @param Traversable $items
     * @param array $args
     * @return Generator
     */
    public function apply(Traversable $items, array $args=array());
}

Kullanıcıya sağlayacağımız fırsatın ilk şartı kategori ekseninde olduğuna göre sepet öğelerini kategori bazlı filtreleyecek filtre sınıfını kodlayalım.

<?php

namespace Promotion\Filter;

use Promotion\FilterInterface;

class CategoryFilter implements FilterInterface
{
    /**
     * @param Traversable $items
     * @param array $args
     * @return Generator
     */
    public function apply(Traversable $items, array $args=array())
    {
        foreach($items as $item) {
            if( $item->getCategory()->getId() == $args['category_id'] ) {
                yield $item;
            }
        }
    }
}

Bir sonraki ölçütümüz olan tutar bilgisi için yeni bir filtre sınıfı oluşturalım.

<?php

namespace Promotion\Filter;

use Promotion\FilterInterface;

class PriceFilter implements FilterInterface
{
    const CRITERIA_EQUAL = 'eq';
    const CRITERIA_LESS_THAN = 'lt';
    const CRITERIA_GREATER_THAN = 'gt';
    const CRITERIA_LESS_THAN_OR_EQUAL = 'lte';
    const CRITERIA_GREATER_THAN_OR_EQUAL = 'gte';

    /**
     * @param Traversable $items
     * @param array $args
     * @return Generator
     */
    public function apply(Traversable $items, array $args=array())
    {
        foreach($items as $item) {
            switch($args['condition']) {
                case self::CRITERIA_EQUAL:
                    if($item->getPrice() == $args['price']) {
                        yield $item;
                    }
                    break;
                case self::CRITERIA_LESS_THAN:
                    if($item->getPrice() < $args['price']) {
                        yield $item;
                    }
                    break;
                case self::CRITERIA_GREATER_THAN:
                    if($item->getPrice() > $args['price']) {
                        yield $item;
                    }
                    break;
                case self::CRITERIA_LESS_THAN_OR_EQUAL:
                    if($item->getPrice() <= $args['price']) {
                        yield $item;
                    }
                    break;
                case self::CRITERIA_GREATER_THAN_OR_EQUAL:
                    if($item->getPrice() >= $args['price']) {
                        yield $item;
                    }
                    break;
            }
        }   
    }
}

Son olarak her iki şartı VE lojiğine tabi tutacak filtre sınıfını oluşturalım.

<?php

namespace Promotion\Filter;

use Promotion\FilterInterface;

class AndFilter implements FilterInterface
{
    /**
     * @var FilterInterface $filter1
     */
    private $filter1;

    /**
     * @var FilterInterface $filter2
     */
    private $filter2;

    /**
     * @param FilterInterface $filter1
     * @param FilterInterface $filter2
     */
    public function AndFilter(FilterInterface $filter1, FilterInterface $filter2)
    {
        $this->filter1 = $filter1;
        $this->filter2 = $filter2;
    }

    /**
     * @param Traversable $items
     * @param array $args
     * @return Generator
     */
    public function apply(Traversable $items, array $args=array())
    {
        $filtered = $this->filter1->apply($items, $args);
        return $this->filter2->apply($filtered, $args);
    }
}

Filtrelerimiz hazır olduğuna göre bir önceki örnekde oluşturduğumuz SpecialOfferCalculator sınıfını yeni şartlara göre düzenleyelim.

<?php
namespace Calculator\Calculator;

use Calculator\Calculator\CalculatorAbstract;
use Promotion\Filter\CategoryFilter;
use Promotion\Filter\PriceFilter;
use Promotion\Filter\AndFilter;

class SpecialOfferCalculator extends CalculatorAbstract
{
    const OFFER_LIMIT = 3;
    const OFFER_CATEGORY_ID = 5; // CATEGORY_ID:5 = Yeni Yıl
    const OFFER_PRICE_LIMIT = 100;
    const OFFER_PRICE_COND = PriceFilter::CRITERIA_GREATER_THAN_OR_EQUAL;

    private prepareItems()
    {
        $filter = new AndFilter(new CategoryFilter(), new PriceFilter());
        $filteredItems = $filter->apply(
            $this->decoratedObject->getItems(), 
            array(
                'category_id' => self::OFFER_CATEGORY_ID,
                'price'       => self::OFFER_PRICE_LIMIT,
                'condition'   => self::OFFER_PRICE_COND
            )
        );

        // Generator tip, iterate edilmedigi surece sonuc donmedigi icin,
        // bir sonraki metodda yapilacak belirli kategorideki urun sayisi
        // kontrolu nedeniyle generator tipini diziye donusturuyoruz.
        $items = array();
        foreach($filteredItems as $item) {
            $items[] = $item;
        }
        return $items;
    }

    public function getTotalPrice()
    {
        $totalPrice = $this->decoratedObject->getTotalPrice();
        $items = $this->prepareItems();

        if(count($items) > self::OFFER_LIMIT) {
            // Urunleri pahalidan ucuza siralayarak 100 TL ve uzeri en ucuz
            // urunun tutarini indirim olarak uyguluyoruz.

            usort($items, function($itemA, $itemB) {
                return $itemA->getPrice() > $itemB->getPrice();
            });

            $totalPrice -= end($items)->getPrice();
        }
        return $totalPrice;
    }
}

Artık sepet tutarımızı hesaplamaya hazırız.

<?php
namespace MyApp;

use Basket\Basket;
use Basket\BasketItem;
use Promotion\Discount;
use Calculator\Calculator\TotalPriceCalculator;
use Calculator\Calculator\SpecialOfferCalculator;
use Calculator\Calculator\DiscountCalculator;

class Main
{
    public function main()
    {
        $basket = new Basket();
        $basket->add(new BasketItem("001", new Category(5, 'Yeni Yıl'), 'Gömlek', 150))
            ->add(new BasketItem("001", new Category(5, 'Yeni Yıl'), 'Gömlek', 180))
            ->add(new BasketItem("001", new Category(5, 'Yeni Yıl'), 'Gömlek', 105))
            ->add(new BasketItem("002", new Category(5, 'Yeni Yıl'), 'Pantolon', 80);
        $basket->setDiscount(new Discount(Discount::DISCOUNT_TYPE_FIXED, 10));
        $basket = new DiscountCalculator(
            new SpecialOfferCalculator(
                new TotalPriceCalculator($basket)
            )
        );
        print $basket->getTotalPrice(); 
        // sub total = 150 + 180 + 105 + 80 = 515
        // ofered total = sub total - 105 = 410
        // Payment total = offered total - discount amount (10) = 405 TL
    }
}
Genel Yazılım ve Sistem Mühendisliği

Dekoratör Tasarım Deseni

Dekoratör tasarım deseni, bir nesne üzerinde yapısal değişiklik gerçekleştirmeden yeni yetenekler kazandırılmak istenildiği durumlarda tercih edilen bir tasarım kalıbıdır. Genellikle tekil sorumluluk ilkesi gereği sorumlulukların müferit sınıflar arasında bölünmek istendiği yerlerde tercih edilir. Belirli bir mal veya hizmetin satışında ara toplam, indirimler, vergiler ve özel durumların hesaplanması istendiği durumlar için son derece elverişli bir yapısal tasarım desenidir.

Gerçek Hayat Örneği:

Bir elektronik ticaret sitesinde aşağıdaki kampsamda hizmetler veirlebilmektedir.

* Kullanıcı sepetine istediği kadar ürün ekleyebilir.
* Sabit tutarlı veya yüzdelik cinsten indirimlerden yararlanabilir.

İstisnalar:

* Kullanıcı A kategorisindeki aynı üründen 3 adet satın alırsa 2 tanesinin parasını öder.

Problem:

Kullanıcı yeni yıl kategorisinden 3 adet gömlek satın almış, hesabına tanımlı olan 10 TL lik indirimden yararlanmıştır.

Uygulama:

Başlamadan önce sepet, ürün ve indirim sınıflarımızı tanımlayalım.

namespace Basket;

class BasketItem
{
	/**
	 * @var string
	 */
	private $code;

	/**
	 * @var Category;
	 */
	private $category;

	/**
	 * @var string
	 */
	private $name;

	/**
	 * @var float
	 */
	private $price;

	public function BasketItem($code, $category, $name, $price)
	{
		$this->code = $code;
		$this->cateogry = $categorY;
		$this->name = $name;
		$this->price = $price;
	}

	/**
	 * @return string
	 */
	public function getCode()
	{
		return $this->code;
	}

	/**
	 * @return Category
	 */
	public function getCategory()
	{
		return $this->category;
	}

	/**
	 * @return string
	 */
	public function getName()
	{
		return $this->name;
	}

	/**
	 * @return float
	 */
	public function getPrice()
	{
		return $this->price;
	}
}
<?php
namespace Basket;

use Basket\BasketItem;
use Promotion\Discount;

class Basket
{
	/**
	 * @var SplObjectStorage
	 */
	private $collection;

	/**
	 * @var float
	 */
	private $totalPrice = 0.0;

	/**
	 * @var \Promotion\Discount
	 */
	private $discount;

	public function Basket()
	{
		$this->collection = new SplObjectStorage();
	}

	/**
	 * @return SplObjectStorage
	 */
	public function getItems()
	{
		return $this->collection;
	}

	/**
	 * @param BasketItem $item
	 * @return Basket
	 */
	public function add(BasketItem $item)
	{
		$this->collection->attach($item);
		return $this;
	}
	
	//...

	public function getTotalPrice()
	{
		return $this->totalPrice;
	}

	public function setTotalPrice($value)
	{
		$this->totalPrice = $value;
	}

	/**
	 * @return \Promotion\Discount
	 */
	public function getDiscount()
	{
		return $this->discount;
	}

	/**
	 * @param \Promotion\Discount $discount
	 */
	public function setDiscount(Discount $discount)
	{
		$this->discount = $value;
	}

	//...
}
<?php
namespace Promotion;

class Discount
{
	DISCOUNT_TYPE_FIXED = 'fixed';
	DISCOUNT_TYPE_PERCENTAGE = 'percentage';

	/**
	 * @var string
	 */
	private $type;
	
	/**
	 * @var float
	 */
	private $amount;

	public function Discount($type, $amount)
	{
		$this->type = $type;
		$this->amount = $amount;
	}

	/**
	 * @return string
	 */	
	public function getType()
	{
		return $this->type;
	}

	/**
	 * @return float
	 */
	public function getAmount()
	{
		return $this->amount;
	}
}

Hesaplama işlemi sırasında kullanıcının sepetini dekore edeceğimize göre sepet ve somut dekoratör sınıfları benzer niteliklere sahip olmalıdır. Bunun için sepet ve dekoratör sınıflarının implement edeceği arayüz sınıfını oluşturalım.

<?php
namespace Calculator;

interface Calculatable
{
	public function getTotalPrice();
}

* Sepet nesnesinin yeni oluşturduğumuz arayüzü implement etmesini sağlıyoruz.

<?php
namespace Basket;

use Basket\BasketItem;
use Calculator\Calculatable;

class Basket implements Calculatable
{
	//...
}

Her dekoratör, kurulum sırasında sepeti veya bir başka dekoratörü argüman olarak alabilir. Birden fazla somut dekoratör sınıfım olacağını varsayarak yeni bir soyut dekoratör sınıfı oluşturuyorum.

<?php
namespace Calculator\Calculator;

use Calculator\Calculatable;

abstract class CalculatorAbstract implements Calculatable
{
	/**
	 * @var Calculatable
	 */
	protected $decoratedObject;

	/**
	 * @var Calculatable $decoratedObject
	 */
	public function CalculatorAbstract(Calculatable $decoratedObject)
	{
		$this->decoratedObject = $decoratedObject;
	}

	public function getItems()
	{
		return $this->decoratedObject->getItems();
	}

	public function getTotalPrice()
	{
		return $this->decoratedObject->getTotalPrice();
	}
}

* Sepet toplamını hesaplayacak yeni bir dekoratör sınıfı oluşturuyorum.

<?php
namespace Calculator\Calculator;

use Calculator\Calculator\CalculatorAbstract;

class TotalPriceCalculator extends CalculatorAbstract
{
	public function getTotalPrice()
	{
		$total = 0;
		foreach($this->decoratedObject->getItems() as $item) {
			$total += $item->getPrice();
		}
		return $toal;
	}
}

* Aynı kategorideki bir üründen 3 adet satın alındığında 2 ürün parası ödemeyi sağlayacak decoratörü geliştirelim.

<?php
namespace Calculator\Calculator;

use Calculator\Calculator\CalculatorAbstract;

class SpecialOfferCalculator extends CalculatorAbstract
{
	const OFFER_LIMIT = 3;

	public function getTotalPrice()
	{
		$totalPrice = $this->decoratedObject->getTotalPrice();
		$groupedProducts = array();

		$isDiscountApplied = false;

		foreach($this->decoratedObject->getItems() as $item) {
			$categoryId = $item->getCategory()->getId();
			
			if(!array_key_exists($groupedProducts, $categoryId)) {
				$groupedProducts[$categoryId] = array();
			}
			
			$groupedProducts[$categoryId][] = $item;

			if(count(groupedProducts[$categoryId]) >= self::OFFER_LIMIT and !$isDiscountApplied) {
				$totalPrice -= $item->getPrice();
				$isDiscountApplied = true;
			}
		}

		return $totalPrice;
	}
}

Yazının başında kullanıcıların, indirimleri yüzde veya sabit tutar cinsinden kullanabileceğinden söz etmiştik. Ancak soyut düşündüğümüzde indirim hesaplamak bizim için nihayi rakama ulaşmaktaki adımlardan biri olup, indirimin yüzde veya sabit tutar cinsinden olması ise indirimin hesaplama metodolojisidir. Bu nedenle indirim hesaplama yöntemini sepet hesaplayıcılardan soyutlamak için strateji tasarım desenini kullanarak yeni bir indirim hesaplayıcı sınıf ailesi oluşturacağız.

<?php
namespace Promotion\Discount;

interface DiscountType
{
	/**
	 * @param float $discountAmount
	 * @param float $price
	 */
	public function calculate($discountAmount, $price);
}
<?php
namespace Promotion\Discount;

use Promotion\Discount\DiscountType;

class FixedDiscount implements DiscountType
{
	/**
	 * @param float $discountAmount
	 * @param float $price
	 */
	public function calculate($discountAmount, $price)
	{
		return $price - $discountAmount;
	}
}
<?php
namespace Promotion\Discount;

use Promotion\Discount\DiscountType;

class PercentageDiscount implements DiscountType
{
	/**
	 * @param float $discountAmount
	 * @param float $price
	 */
	public function calculate($discountAmount, $price)
	{
		return $price - ($price * ($discountAmount / 100));
	}
}
<?php
namespace Promotion;

use Promotion\Discount\DiscountType;

class DiscountContext
{
	/**
	 * @var \Promotion\Discount\DiscountType
	 */
	private $discountType;

	public function DiscountContext(DiscountType $discountType)
	{
		$this->discountType = $discountType;
	}

	public function apply($discountAmount, $price)
	{
		return $this->discountType->calculate($discountAmount, $price);
	}
}

* Şimdi de İndirim tutarını sepet toplamına yansıtacak dekoratör sınıfımızı oluşturalım.

namespace Calculator\Calculator;

use Promotion\Discount;
use Promotion\DiscountContext;

class DiscountCalculator extends CalculatorAbstract
{
	public function getTotalPrice()
	{
		$discount = $this->decoratedObject->getDiscount();
		if($discount->getType() == Discount::DISCOUNT_TYPE_FIXED) {
			$discountType = new \Promotion\Discount\FixedDiscount();
		} elseif($discount->getType() == Discount::DISCOUNT_TYPE_PERCENTAGE) {
			$discountType = new \Promotion\Discount\PercentageDiscount();
		} else {
			throw new Exception('Bad discount defination.');
		}
		$context = new DiscountContext($discountType);
		return = $context->apply($discount->getAmount(), $this->decoratedObject->getTotalPrice());
	}
}

Artık sepet tutarımızı hesaplamaya hazırız.

<?php
namespace MyApp;

use Basket\Basket;
use Basket\BasketItem;
use Promotion\Discount;
use Calculator\Calculator\TotalPriceCalculator;
use Calculator\Calculator\SpecialOfferCalculator;
use Calculator\Calculator\DiscountCalculator;

class Main
{
	public function main()
	{
		$basket = new Basket();
		$basket->add(new BasketItem("001", new Category(1, 'Giyim'), 'Gömlek', 80))
			->add(new BasketItem("001", new Category(1, 'Giyim'), 'Gömlek', 80))
			->add(new BasketItem("001", new Category(1, 'Giyim'), 'Gömlek', 80))
			->add(new BasketItem("002", new Category(1, 'Giyim'), 'Pantolon', 200));
		$basket->setDiscount(new Discount(Discount::DISCOUNT_TYPE_FIXED, 10));
		$basket = new DiscountCalculator(new SpecialOfferCalculator(new TotalPriceCalculator($basket)));
		print $basket->getTotalPrice(); //TotalPrice = 350 TL
	}
}

Genel

Para Yönelimli Programlama (PYP)

Para Yönelimli Programlama (PYP)Para Yönelimli Programlama, programlama eyleminin para ekseninde gerçekleştirildiği yazılım geliştirme faaliyetidir. Geliştiriciler arasında “Dark Side”, “Para Çokomel”, “Parasıyla değil mi ?” sözleriyle de ifade edilen bu yaklaşımı, bu yazıda birlikte irdeleyeceğiz.

PYP, aslında hepimizin bildiği algülüm vergülüm esasına dayanan ticari bir faaliyetten başka birşey değildir. İşverenin, yazılım geliştirme ihtiyaçlarının karşılanması doğrultusunda aylık periyodlarla yaptığı maaş ödemesine istinaden, geliştiricinin işveren beklentilerini makul sürelerde karşılaması esasına dayanır.

Adından da anlaşılacağı üzere programlama faaliyeti asla mantık ekseninde gerçekleştirilmez. İşin içeriği, yaratacağı prestij kaybı, görsel/mantıksal/fonksiyonel problemler geliştiriciyi ilgilendirmez. Ödemeler düzgün gerçekleştiği sürece müşteri her zaman haklıdır. Ödemenin aksaması, rakip veya farklı sektördeki firmanın yapacağı X+1 lik ücret teklifi mevcut firmayla olan ilişkilerin aynı gün sonlanmasına neden olabilir. (Mevcut firmanın x-1 lik personeli temin edebilmesi durumunda mevcut personelin işine aynı gün sonverebildiği gibi.)

PYP uygulayan geliştiricinin egosu ve fikri olmaz. Şayet fikri varsa ısrarcı olmaz. Tartışmaya girmez. Alınacak nihayi cevap her zaman için “Ok o zaman o şekilde yapalım.” şeklindedir. Kıdeme bağlı olarak “parasıyla değil mi ?” gibi türlü varyasyonların da duyulması mümkündür.

Para Yönelimli Programlama (PYP)Bu tip geliştiriciler, yedi köyle barışık denilecek cinsten insanlardır. Sistemcisinden, proje yöneticisine ve diğer tüm ekip arkadaşlarıyla tam anlamıyla uyum içindedir. Asla laf taşımaz. İşinin yürümesi için herşeyi yapar. Nabza göre şerbet verir. İtin köpeğin önde gidenidir. İş bitiricidir. Üretimin süreci ve sonucu göz önünde bulundurulduğunda aslında her PYP geliştiricisi DGYP ve GGYP yaklaşımlarını da benimser. (Bkz. Daya Geç Yönelimli Programlama (DGYP). Bkz. Goygoy Yönelimli Programlama (GGYP)) Projedeki mevcut özellik defalarca kaldırılıp eklense de asla üşenmez, yorum getirmez. İşverenin kendisine bunun için para ödediğinin bilincindedir.

PYP uygulayan geliştirci, bir projenin öyle veya böyle yayına çıkabilmesi adına her firmada en az birkaç tane bulunması gereken geliştirici tipidir. Ego tatminin yaşanacağı yer işyeri olmadığından işverenin yazılım geliştiricinin egosunun tatmin etmek için iş beğendirmek veya her an yeni bir challange yaratmak gibi bir mecburiyeti yoktur. İşveren para karşılığında geliştiricinin bilgisini ve zamanını satın alır.

Pahalıdır. Standart bir yazılım geliştiriciyi X liraya, bir rockstar ı X+1 liraya çalıştırabilirsiniz. Deneyimli bir PYP geliştiricisini X+2 liraya çalıştıramazsınız. Şayet anlaşsanız bile daha iyi şartları sağlayan başka bir firma bulduğu gün yerinde yeller eser.

Sadakat ancak hayvanlarla insanlar arasında yaşanabilecek bir duygudur. Bu nedenle firmanın geliştiriciden sadakat beklentisi son derece yersizdir. PYP geliştiricisi, yukarıda belirtilen ücret politikası nedeniyle asla herhangibir projede tek adam olarak konumlandırılmamalıdır. Çünkü her an herşey olabilir :)

Genel

PHP Geliştiricileri 22 Şubat 2014 de PHPKonf ile Bir Araya Geliyor

PHPKonf22 Şubat 2014 Cumartesi günü düzenlenecek olan PHPKonf etkinliği, web teknolojileriyle ilgilenen meraklıları Bahçeşehir Üniversitesi’nin Beşiktaş’daki kampüsünde bir araya getirecek.

Etkinlik isminin PHP Konferansı olması sizleri yanıltmasın. Zira tecrübeli geliştiricilerden gerçek hayat deneyimleri, teknolojik ve mimari pek çok konuyla dopdolu bir etkinlik, katılımcılarını bekliyor.

Değerli dostumuz Emir Karşıyakalı tarafından organize edilen PHPKonf, etkinlik destekçilerinden paylas.com tarafından canlı olarak yayınlanacak. Etkinlik kaydını Eventbrite üzerinden ücretsiz olarak gerçekleştirebilir, gelişmeleri @phpkonf isimli twitter hesabından takip edebilir, diğer tüm detayları PHPKonf web sitesinden öğrenebilirsiniz.

Elini taşın altına koyup böyle bir etkinliği organize ederek biz geliştiricileri bir araya getirecek olan Emir Karşıyakalı ve tüm destekçilerine gönülden teşekkür ederiz.

Programa Dilleri ve Çatıları

Hadise var dediler, geldik!

HadiseHadise, çok ilkel (basit) bir olay güdümlü programlama kütüphanesidir. Mevcut PHP aplikasyonunuza basitce implemente ederek yazılımınızın akışını tetiklediğiniz olaylar doğrultusunda yönetebilirsiniz.

Hadise kütüphanesinin en güncel sürümünü aşağıdaki github reposunda bulabilirsiniz:
https://github.com/ibrahimgunduz34/hadise

Örneğin projenizin içindeki basit bir api implementasyonuna hadise kütüphanesini uyarlayalım.

Öncelikle olay dinleyecilerimizi kodlayalım.
İlk olarak istek göndermeden önce tetikleyeceğimiz olay dinleyicimizi ile başlayalım.

use \Hadise\IEventListener;
use \Hadise\Event;
class BeforeRequestListener implements IEventListener
{
  public function invoke(Event $event)
  {
    $logData = sprintf("Source:\n" .
                       "------------------\n" .
                       "%s\n" .
                       "Request:\n" .
                       "------------\n" .
                       "%s\n", get_class($event->getSource()),
                       var_export($event->getParameters(), true);
    file_put_contents('transaction_log', $logData, FILE_APPEND);
  }
}

Şimdi de istek gönderdikten sonra tetikleyeceğimiz olay dinleyicimizi kodlayalım.

use \Hadise\IEventListener;
use \Hadise\Event;
class AfterRequestListener implements IEventListener
{
  public function invoke(Event $event)
  {
    $logData = sprintf("Source:\n" .
                       "------------------\n" .
                       "%s\n" .
                       "Response:\n" .
                       "------------\n" .
                       "%s\n", get_class($event->getSource()),
                       var_export($event->getParameters(), true);
    file_put_contents('transaction_log', $logData, FILE_APPEND);
  }
}

Mevcut api implementasyonumuzda istek öncesi ve sonrasında olay dinleyicilerimizin tetiklenmesini sağlayalım.

class MyApiImpl implements MyApiImplInterface
{
  //...
  protected function __sendRequest($url, array $data)
  {
    $requestXML = http_build_query($data);
    EventManager::getManager('MyApiImpl')->riseEvent('beforeRequest', $this, array('data' => $requestXML));
        
    $ch = curl_init();
    //...
    $responseData = curl_exec($ch);
    //...
    EventManager::getManager('MyApiImpl')->riseEvent('afterRequest', $this, array('data' => $responseData));
  }
  //...
}

Son olarak olay dinleyicilerimizi olay yöneticimize iliştirelim.

EventManager::getManager('MyApiImpl')
            ->addListener('beforeRequest', new BeforeRequestListener());
EventManager::getManager('MyApiImpl')
            ->addListener('afterRequest', new AfterRequestListener());

Hepsi bu kadar. İyi eğlenceler :)

Yazılım ve Sistem Mühendisliği

Tasarım, tasmam kime ne ?

Aslında bugün “Bir fonksiyon yazdım-2” başlıklı yazıyla karşınıza çıkmayı hedeflemiştim ancak şu sıra üstüne çok konuştuğum, çokca da içinde bulunduğum projelere b.k atmama vesile olan başka bir konuyla karşınızdayım.

Hepimiz farklı şirketlerde geliştirici olarak değişik projelerde görev alıyoruz. PHP dünyası için konuşmak gerekirse şanslı bir azınlık popüler frameworklerle geliştirilmiş, kurallara/standartlara uygun yazılmış projelerde çalışırken malesef büyük bir çoğunluk kötü kodlanmış, çoğunlukla spagettiye dönmüş kodların içinde debelenip duruyoruz. Bu durum müşteriden gelen isteklere verilen zamansal hedeflerin tutturulamamasına, yeni geliştirmelerin bir şekilde eski geliştirmeleri bozarak yazılımın hatalı çalışmasına ve en kötüsü test edilemeyen kod üretimine neden olmaktadır. Peki doğru bir yazılımın tasarımı nasıl olmalı ?
İyi yazılım tasarımının nasıl olabileceğini açıklayabilmek için öncelikle kötü bir tasarımın ne gibi özelliklere sahip olduğunu bilmeliyiz. Robert Martin abimiz kötü bir yazılım tasarımının 3 temel özelliğe sahip olabileceğini söylemiş.
Rigidity
Rigidity (Esnemezlik):
Kötü tasarımlar geliştirmeye uygun değildir, Esneklikleri yoktur. Birgün, belki birkaç saat süreceğini düşündüğünüz bir iş haftalar sürebilir. İşin ne zaman biteceği kestirilemez. Dolayısıyla tasarım sorunu gün geçtikce yönetimsel bir problem halini alır.

Fragidity
Fragidity (Kırılganlık):
Hepimizin aşine olduğu bir konu. Yeni bir geliştirmenin eski bir geliştirmenin bozulmasına neden olmasıdır. Bu durum, yapılan her geliştirmede çalışan başka bir yerin bozulacağı korkusunu yaratır ki işinizi yapamaz hale gelirsiniz. “IT bir yeri yaparken bir yeri bozuyor be arkadaş” cümlesini sıklıkla duymanıza neden olur.

Immobility
Immobility (Taşınamamazlık) :
Bağımlılıklar nedeniyle bir kodun başka bir yerde kullanılamaması durumudur. Bu tarz durumlarda aynı modülün tekrar kullanılması gerektiği durumlarda modül/kod bloğu bir kere daha yazılır. Lojik değişikliği gerektiği durumlarda pek çok yerde değişiklikyapmanız gerekir, çoğunluklada atlanan/unutulan yerler nedeniyle sistemin stabilitesi bozulur, yazılım hatalı çalışmaya başlar.

Ortalama birgününüz, çözdüğünüz issue lar ve dahil olduğunuz projeler gözünüzün önünden geçti değil mi ? :)

Peki doğru bir tasarım nasıl olmalı ? Doğru tasarım aşağıda kısaca açıklamaya çalıştığım S.O.L.I.D. prensiplerine uygun olmalıdır.

Single Responsibility (Tekil sorumluluk):
Her kod bloğu (modül, sınıf, metod) kendi sorumluluğundaki işi yapmalı. Kod bloklarının kendi sorumluluğu dışında işleri yapması bağımlılığı arttırır. (Bkz. Bi fonksiyon yazdım… )

Open-Close (Açık – Kapalı) :
Modülleriniz gelişime açık, değişime kapalı olmalıdır. Herhangibir değişiklik gereksinimi olduğunda mevcutu değiştirmek yerine yeni bir özellik olarak eklenmelidir.

Liskov (Liskov prensibi) :
Türemiş bir sınıf atası olan sınıfla aynı davranışı sergilemelidir.

Interface Segregation (Arayüz ayırma):
Benzer özellikler taşıyan sınıflardaki metodların gruplanarak tek bir interface e tabi olmaları sağlanmalıdır. Böylece programınız tarafından kullanılmayan metodları projenize dahil etmemiş olursunuz.

Dependency Inversion (Bağımlılıkların azaltılması):
Bağımlılıklar, concreate sınıflardan yapılmak yerine mümkün olduğunca soyut sınıflar ve arayüzler üzerinden gerçekleştirilmelidir.

Yazılım ve Sistem Mühendisliği

Bi Fonksiyon Yazdım…

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.

Yazılım ve Sistem Mühendisliği

Observer Tasarım Kalıbı ve Olay Güdümlü Programlama

Üzerinde çalıştığım bir projeye kısa bir çay molası vermişken sizlerle çok sevdiğim ve şu an üzerinde çalıştığım projede de kullandığım gözlemci (observer) tasarım kalıbı ve olay güdümlü programlama konularndan kısaca bahsetmek istiyorum.

87628254
Olay güdümlü programlama, iç veya dış etkenlerle meydana gelen olaylar doğrultusunda program akışının değiştirilmesi gerektiği durumlarda kullanılan bir tekniktir. Bir e-ticaret sitesinde ziyaretcinin herhangibir nedenle alışverişi tamamlayamaması durumunda belirli bir kullanıcı gurubunun haberdar edilmesi, herhangibir servisle iletişim sorunu olması durumunda aksiyon alınması gibi durumlar, olay güdümlü programlama gerektirir.

Observer
Observer tasarım kalıbı ise birbirleriyle bire-çok ilişki içindeki nesnelerden herhangibirinde değişiklik olması durumunda bir merkez üzerinden diğer nesnelerin haberdar edilmesi gerektiği durumlarda kullanılır ve olay güdümlü programlama için biçilmiş kaftandır :)

Dilerseniz gelin hemen birlikte gerçek hayattan bir örnek inceleylim.

Bir elektronik ticaret şirketinde çalışıyoruz. Saat başı zamanlanmış bir görevle gelen siparişleri lojistik şirketine iletiyoruz. Ancak siparişlerin herhangibir nedenle iletilememesi durumunda 1 dk bekleyip tekrar deneme gerçekleştirmek istiyoruz.

Kod örneğine geçmeden önce gözlemci tasarım kalıbı ve olay güdümlü programlama arasındaki ilişki konusunda kısa bir bilgi vermekte yarar var. Observer tasarım desenini kullanarak yazılımımızın akışıını olaylarla değiştirebilmek için olayın gerçekleştiği yeri merkez, olayı dinleyen bölümü observer olarak düşünmeliyiz.

Aşağıdaki gibi bir lojistik arayüzümüz var. Tüm lojistik implementasyonlarımız bu arayüzü implement eder.

interface ILogistic
{
	/**
	 * @param string $articleCode
	 * @param string $refNumber
	 * @return bool
	 */
	public function send($articleCode, $refNumber);
	
	/**
	 * @param string $refNum
	 * @return string
	 */
	public function getStatus($refNum);
}

Basit ve anlaşılır olması açısından herbir gözlemcinin tek bir olay içerdiğini varsayıyoruz. Aşağıda gözlemci sınıflarımızın implement edeceği arayüz kodu yeralıyor.

interface IObserver
{
	/**
	 * @param Event $event
	 */
	public function invoke(Event $event);
}

Olayın gerçekleşeceği bölümün implement edeceği IObservable arayüzünü kodlayalım.

interface IObservable
{
	/**
	 * @param IObserver $observer
	 * @param string $eventName
	 */
	public function attach(IObserver $observer, $eventName);
	
	/**
	 * @param Event $event
	 */
	public function fireEvent(Event $event);
}

Olay gerçekleştiğinde olayla ilgili bilgileri gözlemciye (observer) taşıyacak olay sınıfımızı kodlayalım.

class Event
{
	/**
	 * @var string
	 */
	private $_event_type;
	
	/**
	 * @var IObservable
	 */
	private $_source;
	
	/**
	 * @var string
	 */
	private $_last_called_method;
	
	/**
	 * @var array
	 */
	private $_arguments;
	
	/**
	 * @param string $eventName
	 * @param IObservable $source
	 * @param string $lastCalledMethod
	 * @param array $arguments
	 */
	public function __construct($eventName,  IObservable $source, $lastCalledMethod, array $arguments)
	{
		$this->_event_name = $eventName;
		$this->_source = $source;
		$this->_last_called_method = $lastCalledMethod;
		$this->_arguments = $arguments;
	}
	
	/**
	 * @return string
	 */
	public function getEventName()
	{
		return $this->_event_type;
	}
	
	/**
	 * @return IObservable
	 */
	public function getSource()
	{
		return $this->_source;
	}
	
	/**
	 * @return string
	 */
	public function getLastCalledMethod()
	{
		return $this->_last_called_method;
	}
	
	/**
	 * @return array
	 */
	public function getArguments()
	{
		return $this->_arguments;
	}
}

Lojistik implementasyonlarımızın devralacağı soyut sınıfımızı kodlayalım.

abstract class LogisticAbstract
{	
	/**
	 * @param string $articleCode
	 * @param string $refNum
	 * @return array
	 */
	abstract protected function _getSendRequest($articleCode, $refNum);
	
	/**
	 * @param string $refNum
	 * @return array
	 */
	abstract protected function _getStatusRequest($refNum);
	
	/**
	 * @param string $apiResponse
	 * @return bool
	 */
	abstract protected function _parseSendRequest($apiResponse);
	
	/**
	 * @param string $apiResponse
	 * @return string
	 */
	abstract protected function _parseStatusRequest($apiResponse);
	
	/**
	 * @param string $url
	 * @param array $data
	 * @throws Exception
	 * @return string
	 */
	protected function _sendRequest($url, array $data)
	{
		$postData = http_build_query($data);
		$ch = curl_init();
		$options = array(CURLOPT_URL => $url,
					    CURLOPT_RETURNTRANSFER => true,
					    CURLOPT_POST 	   => true,
					    CURLOPT_POSTFIELDS 	   => $postData);
		curl_setopt_array($ch, $options);
		$response = curl_exec($ch);
	    if($error = curl_errno($ch)) {
	    	throw new Exception('Error occurred while sending data to remote server. Detail:' . $error, '');
	    }
	    return $response;
	}
}

Lojistik şirketimizin adı örnek lojistik olsun.

class OrnekLojistik extends LogisticAbstract implements ILogistic, IObservable
{
	/**
	 * @var array
	 */
	private $_observers = array();
	
	/**
	 * @param string $articleCode
	 * @param string $refNumber
	 * @return array
	 */
	protected function _getSendRequest($articleCode, $refNumber)
	{
		$xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
				<OrnekLogisticRequest>
				  <transaction>
				  <type>send</type>
				  <articleCode>{$articleCode}</articleCode>
				  <refNum>{$refNumber}</refNum>
				  </transaction>
				</OrnekLogisticRequest>";
		return array('data' => $xml);
	}
	
	/**
	 * @param string $refNum
	 * @return array
	 */
	protected function _getStatusRequest($refNum)
	{
		//...
	}
	
	/**
	 * @param string $apiResponse
	 */
	protected function _parseSendRequest($apiResponse)
	{
		try {
			$xml = new SimpleXMLElement($xml);
			return $xml->Response->Status == 'OK';		
		} catch(Exception $e) {
			throw new Exception('Unexpected Response. Response message:' . $apiResponse);
		}
	}
	
	/**
	 * @param string $apiResponse
	*/
	protected function _parseStatusRequest($apiResponse)
	{
		//...
	}
	
	/**
	 * @param string $articleCode
	 * @param string $refNumber
	 */
	public function send($articleCode, $refNumber)
	{
		try {
			$request = $this->_getSendRequest($articleCode, $refNumber);
			$apiResponse = $this->_sendRequest('http://ws.orneklojistik.com/OrnekWS.php', $request);
			$response = $this->_parseSendRequest($apiResponse);
			if(! $response ) {
				$this->fireEvent(new Event('shipping_failed', $this, __METHOD__, func_get_args() ));
			}
			return $response;
		} catch(Exception $e) {
			$this->fireEvent(new Event('shipping_failed', $this, __METHOD__, func_get_args() ));
                        return false;
		}
	}
	
	/**
	 * @param string $refNum
	*/
	public function getStatus($refNum)
	{
		//...		
	}
	
	/**
	 * (non-PHPdoc)
	 * @see IObservable::attach()
	 */
	public function attach(IObserver $observer, $eventName)
	{
		if( ! isset( $this->_observers[$eventName] ) ) {
			$this->_observers[$eventName] = array();
		}
		$this->_observers[$eventName][] = $observer;
	}
	
	/**
	 * (non-PHPdoc)
	 * @see IObservable::fireEvent()
	 */
	public function fireEvent(Event $event)
	{
		if( isset( $this->_observers[$event->getEventName() ] ) ) {
			/* @var $event IObserver  */
			foreach ($this->_observers[$event->getEventName()] as $observer) {
				$observer->invoke($event);
			}
		}
	}
}

Lojistik şirketine gönderim sırasında sorun yaşamamız durumunda çalışacak gözlemci sınıfını kodlayalım.

class OrnekLojistik_Observer_OnFail implements IObserver
{
	/**
	 * @var bool
	 */
	private $_tried = false;

	/**
	 * (non-PHPdoc)
	 * @see IObserver::invoke()
	 */
	public function invoke(Event $event)
	{
		if( ! $this->_tried ) {
			sleep(60);
			call_user_method($event->getLastCalledMethod(), $event->getSource(), $event->getArguments());
		}	
	}
}

Geldik zamanlanmış görevimize. Buyrun size zamanlanmış görev…

class SendLogisticCommand implements ICommand
{
	/**
	 * @param array $args
	 */
	public function run($args)
	{
		$products = array('ABC123', 'DEF234', 'GHI123');
		
		$logistic = new OrnekLojistik();
		$observer = new OrnekLojistik_Observer_OnFail();
		$logistic->attach($observer, 'shipping_failed');
		
		foreach($products as $product) {
			$logistic->send($product, time() );
		}
	}
}

Biraz uzun oldu sanırım :) Hızlıca hazırladığım için ufak tefek hiyerarşi sıkıntıları olabilir ancak genel anlamda sizlere fikir verebileceğini ümit ediyorum.

Yazılım ve Sistem Mühendisliği

Refactoring üstüne

Bu konu üstüne bir giriş yazısı yazmak istedim ama üstad bana söyleyecek söz bırakmamış. Buyrun önce bunu okuyun sonra da spagetti yazılmış örnek bir kod üzerinde birlikte basitce refactoring gerçekleştirelim.

Aslında konunun özü gayet basit:
“Any fool can write code that a computer can understand.Good programmers write code that humans can understand.” (Martin Fowler)

Her apta bilgisayarın anlayabileceği kodu yazabilir. İyi programcılar insanların anlayabileceği kodu yazarlar.

Kodunuz bu standarda uyana kadar refactor etmeye devam edin. :)

if($_POST['bank_id'] == 1) {
  $mBankXml = 'data=<?xml version="1.0" encoding="utf-8"?>
	<AbankRequest>
	<terminalid>10001</terminalid>
	<merchantid>904567564</merchantid>
	<username>PROVAUT</username>
	<orderid>' . $_POST['orderid'] . '</orderid>
        <amount>' . $_POST['amount'] . '</amount>
	<card>
	<no>' . $_POST['ccno'] .'</no>
	<expdate>' . $_POST['expdate'] . '</expdate>
	<cvc2>' . $_POST['scode'] . '</cvc2>
	</card>
	</AbankRequest>';
	$curl = curl_init();
	$options = array(
		    CURLOPT_URL             => 'http://vpos.abank.com',
		    CURLOPT_POST            => true,
		    CURLOPT_RETURNTRANSFER  => true,
		    CURLOPT_SSL_VERIFYPEER  => false,
		    CURLOPT_SSL_VERIFYHOST  => true,
		    CURLOPT_HEADER          => false,
		    CURLOPT_POSTFIELDS      => $mBankXml
		);
	curl_setopt_array($curl, $options);
	$rd = curl_exec($curl);
	curl_close($curl);
        $xmlObject = simplexml_load_string($rd);
	if($xmlObject->response == 'approved') {
 		$sql = 'UPDATE pos_transactions SET status=1 WHERE sessionid=' . $_SESSION['id'];
		mysql_query($sql);
        } else {
		$sql = 'UPDATE pos_transactions SET status=0 WHERE sessionid=' . $_SESSION['id'];
		mysql_query($sql);
        }
} elseif($_POST['bank_id'] == 2) {
	$mBankXml = 'data=<?xml version="1.0" encoding="utf-8"?>
	<BbankRequest>
	<terminalid>10001</terminalid>
	<merchantid>904567564</merchantid>
	<username>PROVAUT</username>
	<orderid>' . $_POST['orderid'] . '</orderid>
        <amount>' . $_POST['amount'] . '</amount>
	<card>
	<no>' . $_POST['ccno'] .'</no>
	<expdate>' . $_POST['expdate'] . '</expdate>
	<cvc2>' . $_POST['scode'] . '</cvc2>
	</card>
	</BbankRequest>';
	$curl = curl_init();
	$options = array(
		    CURLOPT_URL             => 'http://vpos.bbbank.com',
		    CURLOPT_POST            => true,
		    CURLOPT_RETURNTRANSFER  => true,
		    CURLOPT_SSL_VERIFYPEER  => false,
		    CURLOPT_SSL_VERIFYHOST  => true,
		    CURLOPT_HEADER          => false,
		    CURLOPT_POSTFIELDS      => $mBankXml
		);
	curl_setopt_array($curl, $options);
	$rd = curl_exec($curl);
	curl_close($curl);
        $xmlObject = simplexml_load_string($rd);
	if($xmlObject->response == 'approved') {
 		$sql = 'UPDATE pos_transactions SET status=1 WHERE sessionid=' . $_SESSION['id'];
		mysql_query($sql);
        } else {
		$sql = 'UPDATE pos_transactions SET status=0 WHERE sessionid=' . $_SESSION['id'];
		mysql_query($sql);
        }
} elseif($_POST['bank_id'] == 3) {
	$mBankXml = 'data=<?xml version="1.0" encoding="utf-8"?>
	<CbankRequest>
	<terminalid>10001</termçıkış noktasıinalid>
	<merchantid>904567564</merchantid>
	<username>PROVAUT</username>
	<orderid>' . $_POST['orderid'] . '</orderid>
        <amount>' . $_POST['amount'] . '</amount>
	<card>
	<no>' . $_POST['ccno'] .'</no>
	<expdate>' . $_POST['expdate'] . '</expdate>
	<cvc2>' . $_POST['scode'] . '</cvc2>
	</card>
	</CbankRequest>';
	$curl = curl_init();
	$options = array(
		    CURLOPT_URL             => 'http://vpos.cbank.com',
		    CURLOPT_POST            => true,
		    CURLOPT_RETURNTRANSFER  => true,
		    CURLOPT_SSL_VERIFYPEER  => false,
		    CURLOPT_SSL_VERIFYHOST  => true,
		    CURLOPT_HEADER          => false,
		    CURLOPT_POSTFIELDS      => $mBankXml
		);
	curl_setopt_array($curl, $options);
	$rd = curl_exec($curl);
	curl_close($curl);
        $xmlObject = simplexml_load_string($rd);
	if($xmlObject->response == 'approved') {
 		$sql = 'UPDATE pos_transactions SET status=1 WHERE sessionid=' . $_SESSION['id'];
		mysql_query($sql);
        } else {
		$sql = 'UPDATE pos_transactions SET status=0 WHERE sessionid=' . $_SESSION['id'];
		mysql_query($sql);
        }
} elseif($_POST['bank_id'] == 4) {
	$mBankXml = 'data=<?xml version="1.0" encoding="utf-8"?>
	<DbankRequest>
	<terminalid>10001</terminalid>
	<merchantid>904567564</merchantid>
	<username>PROVAUT</username>
	<orderid>' . $_POST['orderid'] . '</orderid>
        <amount>' . $_POST['amount'] . '</amount>
	<card>
	<no>' . $_POST['ccno'] .'</no>
	<expdate>' . $_POST['expdate'] . '</expdate>
	<cvc2>' . $_POST['scode'] . '</cvc2>
	</card>
	</DbankRequest>';
	$curl = curl_init();
	$options = array(
		    CURLOPT_URL             => 'http://vpos.dbank.com',
		    CURLOPT_POST            => true,
		    CURLOPT_RETURNTRANSFER  => true,
		    CURLOPT_SSL_VERIFYPEER  => false,
		    CURLOPT_SSL_VERIFYHOST  => true,
		    CURLOPT_HEADER          => false,
		    CURLOPT_POSTFIELDS      => $mBankXml
		);
	curl_setopt_array($curl, $options);
	$rd = curl_exec($curl);
	curl_close($curl);
        $xmlObject = simplexml_load_string($rd);
	if($xmlObject->response == 'approved') {
 		$sql = 'UPDATE pos_transactions SET status=1 WHERE sessionid=' . $_SESSION['id'];
		mysql_query($sql);
        } else {
		$sql = 'UPDATE pos_transactions SET status=0 WHERE sessionid=' . $_SESSION['id'];
		mysql_query($sql);
        }
}

Ürkütücü dğeil mi ? Ama reel hayatta malesef bu ve daha da kötüleri de mevcut. Şimdi dilerseniz gelin bu kodları adım adım birlikte refactor edelim.

Öncelikle kodumuzu baştan uca incelediğimizde sayfaya post edilen banka kimliğine göre kodun belirli bir banka api sine veri göndermesi ve sonrasında isteğin sonucu başarılıysa veya başarısızsa veritabanında bir kaydın güncellenmesi gerekiyor.

Her apiye bir http isteği gönderdiğimize göre ilk olarak http isteği yapan bölümü fonksiyon haline getirelim.

/**
  Bu fonksiyon belirli bir url e 
  belirtilen veriyi post eder.

  @param string $url
  @param string $data
  @return string
 */
function sendHttpRequest($url, $data)
{
  $curl = curl_init();
  $options = array(
      CURLOPT_URL             => $url,
      CURLOPT_POST            => true,            
      CURLOPT_RETURNTRANSFER  => true,
      CURLOPT_SSL_VERIFYPEER  => false,   
      CURLOPT_SSL_VERIFYHOST  => true,            
      CURLOPT_HEADER          => false,                   
      CURLOPT_POSTFIELDS      => $data
      );                                                              
  curl_setopt_array($curl, $options);
  $response = curl_exec($curl);
  curl_close($curl);
  return $response
}

Bu değişiklikten sonra kodumuz aşağıdaki hali aldı.

if($_POST['bank_id'] == 1) {
  $mBankXml = 'data=<?xml version="1.0" encoding="utf-8"?>
	<AbankRequest>
	<terminalid>10001</terminalid>
	<merchantid>904567564</merchantid>
	<username>PROVAUT</username>
	<orderid>' . $_POST['orderid'] . '</orderid>
        <amount>' . $_POST['amount'] . '</amount>
	<card>
	<no>' . $_POST['ccno'] .'</no>
	<expdate>' . $_POST['expdate'] . '</expdate>
	<cvc2>' . $_POST['scode'] . '</cvc2>
	</card>
	</AbankRequest>';
	$apiResponse = sendHttpRequest('http://vpos.abank.com', $mBankXml);
        $xmlObject = simplexml_load_string($apiResponse);
	if($xmlObject->response == 'approved') {
 		$sql = 'UPDATE pos_transactions SET status=1 WHERE sessionid=' . $_SESSION['id'];
		mysql_query($sql);
        } else {
		$sql = 'UPDATE pos_transactions SET status=0 WHERE sessionid=' . $_SESSION['id'];
		mysql_query($sql);
        }
} elseif($_POST['bank_id'] == 2) {
	$mBankXml = 'data=<?xml version="1.0" encoding="utf-8"?>
	<BbankRequest>
	<terminalid>10001</terminalid>
	<merchantid>904567564</merchantid>
	<username>PROVAUT</username>
	<orderid>' . $_POST['orderid'] . '</orderid>
        <amount>' . $_POST['amount'] . '</amount>
	<card>
	<no>' . $_POST['ccno'] .'</no>
	<expdate>' . $_POST['expdate'] . '</expdate>
	<cvc2>' . $_POST['scode'] . '</cvc2>
	</card>
	</BbankRequest>';
	$apiResponse = sendHttpRequest('http://vpos.bbank.com', $mBankXml);
        $xmlObject = simplexml_load_string($apiResponse);
	if($xmlObject->response == 'approved') {
 		$sql = 'UPDATE pos_transactions SET status=1 WHERE sessionid=' . $_SESSION['id'];
		mysql_query($sql);
        } else {
		$sql = 'UPDATE pos_transactions SET status=0 WHERE sessionid=' . $_SESSION['id'];
		mysql_query($sql);
        }
} elseif($_POST['bank_id'] == 3) {
	$mBankXml = 'data=<?xml version="1.0" encoding="utf-8"?>
	<CbankRequest>
	<terminalid>10001</terminalid>
	<merchantid>904567564</merchantid>
	<username>PROVAUT</username>
	<orderid>' . $_POST['orderid'] . '</orderid>
        <amount>' . $_POST['amount'] . '</amount>
	<card>
	<no>' . $_POST['ccno'] .'</no>
	<expdate>' . $_POST['expdate'] . '</expdate>
	<cvc2>' . $_POST['scode'] . '</cvc2>
	</card>
	</CbankRequest>';
	$apiResponse = sendHttpRequest('http://vpos.cbank.com', $mBankXml);
        $xmlObject = simplexml_load_string($apiResponse);
	if($xmlObject->response == 'approved') {
 		$sql = 'UPDATE pos_transactions SET status=1 WHERE sessionid=' . $_SESSION['id'];
		mysql_query($sql);
        } else {
		$sql = 'UPDATE pos_transactions SET status=0 WHERE sessionid=' . $_SESSION['id'];
		mysql_query($sql);
        }
} elseif($_POST['bank_id'] == 4) {
	$mBankXml = 'data=<?xml version="1.0" encoding="utf-8"?>
	<DbankRequest>
	<terminalid>10001</terminalid>
	<merchantid>904567564</merchantid>
	<username>PROVAUT</username>
	<orderid>' . $_POST['orderid'] . '</orderid>
        <amount>' . $_POST['amount'] . '</amount>
	<card>
	<no>' . $_POST['ccno'] .'</no>
	<expdate>' . $_POST['expdate'] . '</expdate>
	<cvc2>' . $_POST['scode'] . '</cvc2>
	</card>
	</DbankRequest>';
	$apiResponse = sendHttpRequest('http://vpos.dbank.com', $mBankXml);
        $xmlObject = simplexml_load_string($apiResponse);
	if($xmlObject->response == 'approved') {
 		$sql = 'UPDATE pos_transactions SET status=1 WHERE sessionid=' . $_SESSION['id'];
		mysql_query($sql);
        } else {
		$sql = 'UPDATE pos_transactions SET status=0 WHERE sessionid=' . $_SESSION['id'];
		mysql_query($sql);
        }
}

Farkındayım kod hala bok gibi görünüyor. Şimdi de transaction durumunu güncelleyen veritabanı sorgusu için küçük bir fonksiyon yazalım.

/**
  Bu fonksiyon pos transaction ının 
  durumunu günceller.

  @param integer transactionId
  @param int status
 */
function setTransactionStatus($transactionId, $status)
{
  $transactionId  = (int) $transactionId;
  $status         = (int) $status;
  $sqlText        = "UPDATE pos_transactions SET status={$status} WHERE sessionid={$transactionId}";
  mysql_query($sqlText);
}

Bu fonksiyondan sonra kodumuz aşağıdaki hali alacak.

if($_POST['bank_id'] == 1) {
  $mBankXml = 'data=<?xml version="1.0" encoding="utf-8"?>
	<AbankRequest>
	<terminalid>10001</terminalid>
	<merchantid>904567564</merchantid>
	<username>PROVAUT</username>
	<orderid>' . $_POST['orderid'] . '</orderid>
        <amount>' . $_POST['amount'] . '</amount>
	<card>
	<no>' . $_POST['ccno'] .'</no>
	<expdate>' . $_POST['expdate'] . '</expdate>
	<cvc2>' . $_POST['scode'] . '</cvc2>
	</card>
	</AbankRequest>';
	$apiResponse = sendHttpRequest('http://vpos.abank.com', $mBankXml);
        $xmlObject = simplexml_load_string($apiResponse);
	if($xmlObject->response == 'approved') {
 		setTransactionStatus($_SESSION['id'], 1);
        } else {
		setTransactionStatus($_SESSION['id'], 0);
        }
} elseif($_POST['bank_id'] == 2) {
	$mBankXml = 'data=<?xml version="1.0" encoding="utf-8"?>
	<BbankRequest>
	<terminalid>10001</terminalid>
	<merchantid>904567564</merchantid>
	<username>PROVAUT</username>
	<orderid>' . $_POST['orderid'] . '</orderid>
        <amount>' . $_POST['amount'] . '</amount>
	<card>
	<no>' . $_POST['ccno'] .'</no>
	<expdate>' . $_POST['expdate'] . '</expdate>
	<cvc2>' . $_POST['scode'] . '</cvc2>
	</card>
	</BbankRequest>';
	$apiResponse = sendHttpRequest('http://vpos.bbank.com', $mBankXml);
        $xmlObject = simplexml_load_string($apiResponse);
	if($xmlObject->response == 'approved') {
 		setTransactionStatus($_SESSION['id'], 1);
        } else {
		setTransactionStatus($_SESSION['id'], 0);
        }
} elseif($_POST['bank_id'] == 3) {
	$mBankXml = 'data=<?xml version="1.0" encoding="utf-8"?>
	<CbankRequest>
	<terminalid>10001</terminalid>
	<merchantid>904567564</merchantid>
	<username>PROVAUT</username>
	<orderid>' . $_POST['orderid'] . '</orderid>
        <amount>' . $_POST['amount'] . '</amount>
	<card>
	<no>' . $_POST['ccno'] .'</no>
	<expdate>' . $_POST['expdate'] . '</expdate>
	<cvc2>' . $_POST['scode'] . '</cvc2>
	</card>
	</CbankRequest>';
	$apiResponse = sendHttpRequest('http://vpos.cbank.com', $mBankXml);
        $xmlObject = simplexml_load_string($apiResponse);
	if($xmlObject->response == 'approved') {
 		setTransactionStatus($_SESSION['id'], 1);
        } else {
		setTransactionStatus($_SESSION['id'], 0);
        }
} elseif($_POST['bank_id'] == 4) {
	$mBankXml = 'data=<?xml version="1.0" encoding="utf-8"?>
	<DbankRequest>
	<terminalid>10001</terminalid>
	<merchantid>904567564</merchantid>
	<username>PROVAUT</username>
	<orderid>' . $_POST['orderid'] . '</orderid>
        <amount>' . $_POST['amount'] . '</amount>
	<card>
	<no>' . $_POST['ccno'] .'</no>
	<expdate>' . $_POST['expdate'] . '</expdate>
	<cvc2>' . $_POST['scode'] . '</cvc2>
	</card>
	</DbankRequest>';
	$apiResponse = sendHttpRequest('http://vpos.dbank.com', $mBankXml);
        $xmlObject = simplexml_load_string($apiResponse);
	if($xmlObject->response == 'approved') {
 		setTransactionStatus($_SESSION['id'], 1);
        } else {
		setTransactionStatus($_SESSION['id'], 0);
        }
}

Evet kod çöplüğümüz ufaktan birşeye benzemeye başlıyor. Şimdi de API lere gönderdiğimiz istekleri fonksiyonlaştıralım.

/**
  ABank icin odeme istegi gonderir.
  @param string $orderId
  @param float $amount
  @param string $cardNo
  @param string $cvc2
  @param string $expDate
  @return boolean
 */
function sendABankPaymentRequest($orderId, $amount, $cardNo, $cvc2, $expDate)
{
  $strXml = 'data=<?xml version="1.0" encoding="utf-8"?>
    <AbankRequest>
    <terminalid>10001</terminalid>
    <merchantid>904567564</merchantid>
    <username>PROVAUT</username>
    <orderid>' . $orderId . '</orderid>
    <amount>' . $amount . '</amount>
    <card>
    <no>' . $cardNo .'</no>
    <expdate>' . $expDate . '</expdate>
    <cvc2>' . $cvc2 . '</cvc2>
    </card>
    </AbankRequest>';
  $apiResponse  = sendHttpRequest('http://vpos.abank.com', $strXml);
  $xmlObject    = simplexml_load_string($apiResponse);
  return ($xmlObject->response == 'approved') ? true : false;
}

/**
  BBank icin odeme istegi gonderir.
  @param string $orderId
  @param float $amount
  @param string $cardNo
  @param string $cvc2
  @param string $expDate
  @return boolean
 */
function sendBBankPaymentRequest($orderId, $amount, $cardNo, $cvc2, $expDate)
{
  $strXml = 'data=<?xml version="1.0" encoding="utf-8"?>
    <BbankRequest>
    <terminalid>10001</terminalid>
    <merchantid>904567564</merchantid>
    <username>PROVAUT</username>
    <orderid>' . $orderId . '</orderid>
    <amount>' . $amount . '</amount>
    <card>
    <no>' . $cardNo .'</no>
    <expdate>' . $expDate . '</expdate>
    <cvc2>' . $cvc2 . '</cvc2>
    </card>
    </BbankRequest>';
  $apiResponse  = sendHttpRequest('http://vpos.bbank.com', $strXml);
  $xmlObject    = simplexml_load_string($apiResponse);
  return ($xmlObject->response == 'approved') ? true : false;
}

/**
  CBank icin odeme istegi gonderir.
  @param string $orderId
  @param float $amount
  @param string $cardNo
  @param string $cvc2
  @param string $expDate
  @return boolean
 */
function sendCBankPaymentRequest($orderId, $amount, $cardNo, $cvc2, $expDate)
{
  $strXml = 'data=<?xml version="1.0" encoding="utf-8"?>
    <CbankRequest>
    <terminalid>10001</terminalid>
    <merchantid>904567564</merchantid>
    <username>PROVAUT</username>
    <orderid>' . $orderId . '</orderid>
    <amount>' . $amount . '</amount>
    <card>
    <no>' . $cardNo .'</no>
    <expdate>' . $expDate . '</expdate>
    <cvc2>' . $cvc2 . '</cvc2>
    </card>
    </CbankRequest>';
  $apiResponse  = sendHttpRequest('http://vpos.bbank.com', $strXml);
  $xmlObject    = simplexml_load_string($apiResponse);
  return ($xmlObject->response == 'approved') ? true : false;
}

/**
  DBank icin odeme istegi gonderir.
  @param string $orderId
  @param float $amount
  @param string $cardNo
  @param string $cvc2
  @param string $expDate
  @return boolean
 */
function sendDBankPaymentRequest($orderId, $amount, $cardNo, $cvc2, $expDate)
{
  $strXml = 'data=<?xml version="1.0" encoding="utf-8"?>
    <DbankRequest>
    <terminalid>10001</terminalid>
    <merchantid>904567564</merchantid>
    <username>PROVAUT</username>
    <orderid>' . $orderId . '</orderid>
    <amount>' . $amount . '</amount>
    <card>
    <no>' . $cardNo .'</no>
    <expdate>' . $expDate . '</expdate>
    <cvc2>' . $cvc2 . '</cvc2>
    </card>
    </DbankRequest>';
  $apiResponse  = sendHttpRequest('http://vpos.bbank.com', $strXml);
  $xmlObject    = simplexml_load_string($apiResponse);
  return ($xmlObject->response == 'approved') ? true : false;
}

Ve… kodumuzun yeni hali…

if($_POST['bank_id'] == 1) {
  $bankResponse = sendABankPaymentRequest($_POST['orderId'], 
                                          $_POST['amount'], 
                                          $_POST['ccno'], 
                                          $_POST['cvc2'], 
                                          $_POST['expdate']);
  if($bankResponse) {
    setTransactionStatus($_SESSION['id'], 1);
  } else {
    setTransactionStatus($_SESSION['id'], 0);
  }
} elseif($_POST['bank_id'] == 2) {
  $bankResponse = sendBBankPaymentRequest($_POST['orderId'], 
                                          $_POST['amount'], 
                                          $_POST['ccno'], 
                                          $_POST['cvc2'], 
                                          $_POST['expdate']);
  if($bankResponse) {
    setTransactionStatus($_SESSION['id'], 1);
  } else {
    setTransactionStatus($_SESSION['id'], 0);
  }
} elseif($_POST['bank_id'] == 3) {
  $bankResponse = sendCBankPaymentRequest($_POST['orderId'], 
                                          $_POST['amount'], 
                                          $_POST['ccno'], 
                                          $_POST['cvc2'], 
                                          $_POST['expdate']);
  if($bankResponse) {
    setTransactionStatus($_SESSION['id'], 1);
  } else {
    setTransactionStatus($_SESSION['id'], 0);
  }
} elseif($_POST['bank_id'] == 4) {
  $bankResponse = sendDBankPaymentRequest($_POST['orderId'], 
                                          $_POST['amount'], 
                                          $_POST['ccno'], 
                                          $_POST['cvc2'], 
                                          $_POST['expdate']);
  if($bankResponse) {
    setTransactionStatus($_SESSION['id'], 1);
  } else {
    setTransactionStatus($_SESSION['id'], 0);
  }
}

Eskiye oranla kodumuz oldukça kısa ve okunaklı oldu. Ancak yine de Aynı işi yapan blokların tekrar etmesi biraz can sıkıcı. Dilerseniz gelin şimdi de bankalara istek gönderen fonksiyonları tekilleştirelim ve sonuca göre bir kerede pos transaction durumumuzu güncelleyelim.

/**
  kimliği belirtilen bankaya ödeme isteği gönderir.

  @param int bankId
  @param string $orderId
  @param float $amount
  @param string $cardNo
  @param string $cvc2
  @param string $expDate
  @return boolean
 */

function sendPaymentRequest($bankId, $orderId, $amount, $cardNo, $cvc2, $expDate)
{
  $functionMap = array(
        1 => 'sendABankPaymentRequest',
        2 => 'sendBBankPaymentRequest',
        3 => 'sendCBankPaymentRequest',
        4 => 'sendDBankPaymentRequest'
      );

  if(!in_array($bankId, $functionMap)) {
    return false;
  }
  
  $function = $functionMap[$bankId];

  return $$function($_POST['orderId'],
      $_POST['amount'],
      $_POST['ccno'],
      $_POST['cvc2'],
      $_POST['expdate']);
}

Bu geliştirmeden sonra kodumuz son olarak aşağıdaki hali alacaktır.

$bankResponse = sendPaymentRequest($_POST['bank_id'],
    $_POST['orderId'], 
    $_POST['amount'], 
    $_POST['ccno'], 
    $_POST['cvc2'], 
    $_POST['expdate']);
if($bankResponse) {
  setTransactionStatus($_SESSION['id'], 1);
} else {
  setTransactionStatus($_SESSION['id'], 0);
}

Berker’in comment’inden sonra gelen edit:

$bankResponse = sendPaymentRequest($_POST['bank_id'],
    $_POST['orderId'], 
    $_POST['amount'], 
    $_POST['ccno'], 
    $_POST['cvc2'], 
    $_POST['expdate']);
setTransactionStatus($_SESSION['id'], (int) $bankResponse);

Göktuğ’nun comment’inden sonra sendPaymentRequest() fonksiyonuna gelen edit:

/**
  kimliği belirtilen bankaya ödeme isteği gönderir.

  @param int bankId
  @param string $orderId
  @param float $amount
  @param string $cardNo
  @param string $cvc2
  @param string $expDate
  @return boolean
 */

function sendPaymentRequest($bankId, $orderId, $amount, $cardNo, $cvc2, $expDate)
{
  $functionMap = array(
        1 => 'sendABankPaymentRequest',
        2 => 'sendBBankPaymentRequest',
        3 => 'sendCBankPaymentRequest',
        4 => 'sendDBankPaymentRequest'
      );

  if(!in_array($bankId, $functionMap)) {
    return false;
  }

  return $functionMap[bankId]($orderId,
      $amount,
      $cardNo,
      $cvc2,
      $expDate);
}
Yazılım ve Sistem Mühendisliği

Geliştirme Metodolojisi Finansal Bir Karardır

Yıllar geçse de, yeni teknolojiler çıksa da PHP gibi serbest yazım stillerine izin veren diller olduğu sürece MVC öcü mü ? oop mi spagetti mi gibi tartışmalar daha uzun yıllar devam edecektir…

***

İşin özü, projenizin programlama stilini belirlemek veya değiştirmek, tamamen finansal bir karardır. Aynen vadeli veya peşin para ile alışveriş yapmak gibi.

Prosedürel programlama kolaydır, fonksiyon kütüphaneleri ve kategorize edilmiş klasör yapılarıyla sizden bekleneni hızlıca üreterek işvereninizi ve yöneticilerinizi kolayca memnun etmenizi sağlar. Bazı durumlarda mvc frameworkler ile geliştirilen projelere göre daha az kaynak tükettiği ve daha hızlı aplikasyonların geliştirilmesine elverişli olduğu konusu gerçektir. Ancak kalabalıklaşan ve projenin geneli hakkında bilgisi olmayan ekiplerin proje üzerinde çalışması ve projenin yaşlanması gibi faktörler kod tekrarına; çok basit ancak projenin geneline etki etmesi beklenen geliştirmeler bazen günler sürerek gereksiz geliştirici kaynağı tüketimine neden olabilir.

MVC frameworkler, umumiyetle nesne yönelimli programlama bilgisi gerektiren yapılardır. Gücü yada hızı kullandığınız framework un dispatch cycle ına göre değişkenlik gösterir. Deneyimli developer lar ve tasarım bilgisi gerektirir. Yapılacak her geliştirme programatik ortamda doğru modellenmeli ve dökümante edilmelidir. Concurrent kullanımın artması gibi durumlarda sunucunuz üzerindeki yükün ciddi manada artacağı konusu bir hakikattir. Kısa vadede bu ve benzeri durumlar gözünüzü korkutsada uzun vadede mvc framework lerin sağlayacağı yararların tartışılmazlığı kesindir.

MVC frameworklerin kullanımıyla ilgili olarak başta bahsettiğim concurrent kullanıcı sayısından doğan yük, doğru ölçeklenmiş, dağıtık bir yapıda rahatlıkla karşılanabilir. Ayrıca mvc hiyararşisi, basit manada tüm business logic in model katmanında, arayüz içeriğinin view katmanında ve bu iki katman arasındaki iletişimi sağlayan yapının controller katmanında olmasını gerektirir. Böylelikle sistemin fonksiyonel bölümünü görsel bölümden izole etmiş olursunuz. Projenin geneline etki etmesi beklenen geliştirmeler, mvc frameworklerin sunduğu plugin gibi yapılarla son derece basit ve kısa sürelerde gerçekleştirilebilir(Zend framework’ün controller plugin leri bu konuya iyi bir örnektir.). DbTable ve ORM gibi, frameworklerle birlikte gelen yada uyumlu çalışabilen veri erişimi sistemleri, injection gibi tehlikeli durumlardan sizi koruyarak güvenli sistemler geliştirmenize olanak sağlar. Son olarak her framework kendi kod standartlarıyla birlikte gelir. Kurallara uygun yazılan, düzgün modellenmiş ve dokümante edilmiş sistemler, minimum kod tekrarıyla temiz ve anlaşılır kod yazmanıza olanak sağlar.

***

Ve… emin olun sizden alelacele iş bekleyerek, sizi sistemi çöp haline getirmeye zorlayan her işveren veya yönetici, uzayan development süreleri ve büyüyen maliyetlerden bezerek “Framework diye birşey varmış…” diyecek ve konuyu gündemine alacaktır.