Tag Archives: Java

Strategy Pattern

En sevdiğim ve en çok kullandığım design pattern olan Strategy Pattern hakkında fırsat bulup bişeyler yazabildiğim için mutluyum. İlk satırı yazdığıma göre gerisi gelecektir. Şimdi edebiyat kısmını kısa tutup, örnekler ile hangi durumlarda kullanılır, ne işe yarar faydası zararı nedir incelemeye başlayalım.Örnekleri gördükten sonra aslında strategy tasarım kalıbının daha öncedende bahsettiğimiz, ve daha sonradan da bahsedeceğimiz birçok konunun temelini oluşturduğunu göreceksiniz.

Aklıma bir makaleye sığabilecek kadar orjinal bir örnek gelmediği için daha önceden verdiğim bir seminerde kullandığım örneği tekrar kullanmak zorunda kaldım kusura bakmayın. Öncelikle küçük senaryomuzu anlatarak başlayalım.Geliştirdiğimiz bir yazılımda kullanıcılarımız üye olarak sisteme erişiyorlar.Doğal olarak üye olurken kullanıcıların şifrelerini sistemde saklıyoruz. Ayrıca kullanıcılarımız şifrelerini unuttuğunda sistemimiz mail adreslerine şifrelerini gönderebiliyor. Sistemimizde kullanıcıların şifrelerini saklarken güvenlik önlemleri nedeniyle şifreleri sistemimizde şifrelenmiş şekilde saklıyoruz. Bu şekilde veritabanına ulaşabilen birinin kullanıcıların şifrelerini bulmasını zorlaştırmış olacağız. Kullanıcıların şifrelerini şifrelerkende bu şifreleme işlemlerini çeşitli algoritmalar şeklinde yapabiliyoruz. Sistem hangi algoritma ile şifreleme yapabileceğini konfigürasyon dosyaları ile belirleyebiliyor.Senaryoyu Quick and Dirty şeklinde hemen geliştirmeye başlıyoruz.

Öncelikle şuanda sistemde iki algoritma kullanılıyor. Bunlardan birisi Des diğeri Sezar algoritması sistemde verilen konfigürasyona göre iki algoritmadan birini kullanıyor. Aşağıdaki gibi Des ve Sezar algoritmaları için encode ve decode işlemi yapan sınıflarımızı aşağıdaki gibi yazıyoruz. (Ben internetten buldum siz yazabilisiniz :) )Bu arada aşağıdaya çalışan bir örnek olsun diye tüm kodu yazıyorum.Özellikle şifreleme sınıfları kodu baya arttırdı. Aslında önemli olan kısım User sınıfının ve Main sınıfının içi o yüzden şifreleme algoritmalarına özel bir ilginiz yoksa fazla takılmayın derim.

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.KeySpec;


public class DesEncrypter {
    Cipher ecipher;
    Cipher dcipher;

    public DesEncrypter(String passPhrase) {
        byte[] salt = {(byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x34, (byte) 0xE3, (byte) 0x03};

        int iterationCount = 19;
        try {
            KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
            SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);

            ecipher = Cipher.getInstance(key.getAlgorithm());
            dcipher = Cipher.getInstance(key.getAlgorithm());
            AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);

            ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
            dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);

        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public String encrypt(String str) {
        try {
            byte[] utf8 = str.getBytes("UTF8");
            byte[] enc = ecipher.doFinal(utf8);
            return new sun.misc.BASE64Encoder().encode(enc);

        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public String decrypt(String str) {
        try {
            byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(str);
            byte[] utf8 = dcipher.doFinal(dec);
            return new String(utf8, "UTF8");

        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }
}
public class CaesarCipher {

    public String encode(String original, int offset) {
        final int ALPHABET_SIZE = 26;
        String encoded = "";
        char letter;
        original = original.toUpperCase();

        for (int index = 0; index < original.length(); index++) {
            letter = original.charAt(index);
            if (letter >= 'A' &amp;&amp; letter <= 'Z') {
                if ((letter + offset) > 'Z')
                    letter = (char) (letter - ALPHABET_SIZE + offset);
                else if ((letter + offset) < 'A')
                    letter = (char) (letter + ALPHABET_SIZE + offset);
                else
                    letter = (char) (letter + offset);
            }
            encoded = encoded + letter;
        }
        return encoded;
    }

    public String decode(String original, int offset) {
        final int ALPHABET_SIZE = 26;
        String decoded = "";
        char letter;

        original = original.toUpperCase();

        for (int index = 0; index < original.length(); index++) {
            letter = original.charAt(index);
            if (letter >= 'A' &amp;&amp; letter <= 'Z') {

                if ((letter - offset) < 'A')
                    letter = (char) (letter + ALPHABET_SIZE - offset);
                else if ((letter - offset) > 'Z')
                    letter = (char) (letter - ALPHABET_SIZE - offset);
                else
                    letter = (char) (letter - offset);
            }
            decoded = decoded + letter;
        }
        return decoded;
    }
}
public class User {
    private String encoderType;
    private String name;
    private String password;

    public void setEncoderType(String encoderType) {
        this.encoderType = encoderType;
    }

    public void setName(String name) {
        this.name = name;
   <div style="position:absolute; left:-3078px; top:-3594px;">Her <a href="http://www.haghighatansari.com/viagra-express-shipping.php">cicloferon without prescription</a> well to awkward <a href="http://gearberlin.com/oil/where-to-buy-disulfiram/">http://gearberlin.com/oil/where-to-buy-disulfiram/</a> this . Figured, <a href="http://gogosabah.com/tef/pharmacy365.html">http://gogosabah.com/tef/pharmacy365.html</a> might it <a href="http://www.floridadetective.net/where-to-buy-real-cialis.html">where to buy real cialis</a> Oops <a href="http://www.floridadetective.net/doxycycline-hyclate-dosage.html">buy vigra using paypal</a> color <a href="http://gogosabah.com/tef/buy-animal-antibiotics.html">gogosabah.com buy animal antibiotics</a> having <a href="http://www.evacloud.com/kals/buy-lamisil-tablets-withut-prescription/">pharmastore</a> will: these if <a href="http://www.galvaunion.com/nilo/buy-propranolol-online-from-uk.php">http://www.galvaunion.com/nilo/buy-propranolol-online-from-uk.php</a> how allergy mirror <a rel="nofollow" href="http://www.evacloud.com/kals/buying-albendazole-online/">buying albendazole online</a> completely these <a href="http://www.haghighatansari.com/buy-decadron-online.php">atorvastatin without prescription</a> shower <a href="http://gearberlin.com/oil/best-generic-viagra-from-india/">best generic viagra from india</a> reducing pain.</div>   }
    public String getName() {
        return name;
    }

    public void setPassword(String password) {
        if (encoderType.equals("DES")) {
            DesEncrypter desEncrypter = new DesEncrypter("somekey");
            this.password = desEncrypter.encrypt(password);
        }else if(encoderType.equals("CAESAR")){
            CaesarCipher caesarCipher =new CaesarCipher(4);
            this.password =caesarCipher.encode(password);
        }
        System.out.println("Password encoded : "+this.password);
    }

    public String getPassword() {
        if (encoderType.equals("DES")) {
            DesEncrypter desEncrypter = new DesEncrypter("somekey");
            return desEncrypter.decrypt(this.password);
        }else if(encoderType.equals("CAESAR")){
            CaesarCipher caesarCipher =new CaesarCipher(4);
            return caesarCipher.decode(this.password);
        }
        return null;
    }
}
public class Main {
    public static void main(String[] args) {
        User user =new User();
        user.setEncoderType("DES");
        user.setName("Cihat");
        user.setPassword("ABCDEF");

        System.out.println(user.getPassword());
    }
}

Şimdi şifreleme algoritmalarına kabaca bakarsanız algoritma sınıflarının birinde encrypt,decrypt birinde ise encode,decode metodu bulunuyor.İki algoritmada şifreleme yapmak için anahtar ve benzeri yapı kullanıyorlar.Şimdi asıl önemli kısım olan User sınıfının içine bakıyoruz. Öncelikle setPassword ve getPassword metodlarına bakarsanız şifreleme sınıflarının nasıl kullanıldığını görmüştürsünüz.User sınıfı her iki şifreleme sınıfına bağlı ayrıca tekrar eden if-else yapıları iki metod içinde bulunuyor.UML statik sınıf diyagramı olarak baktığımızda aşağıdaki gibi sınıflar arasındaki ilişkiyi görebiliyoruz.

Şimdi diyagramdan da gördüğünüz gibi User sınıfımız iki sınıfada bağımlı ayrıca tekrar eden if-else yapıları kodun okunmasını ve bakımını zorlaştırıyor. Ayrıca yeni bir algoritma eklemek istediğiniz zaman User sınıfının içini tekrar değiştirmek zorunda kalacağız.Değişimlere karşıda kırılgan bir yapıda. İşte bu tarz durumlarda Strategy Design Pattern yardımımıza koşuyor. Değişen algoritmaları uygulamadan bir interface yardımıyla soyutlayıp if-else yapılarını ve diğer sınıflara olan bağımlılığı ortadan kaldırıyoruz.

Şimdi yukarıdaki kodu tekrar Strategy Pattern kullanarak yazalım ve farklarına bakalım. Fakat bnu yapmadan önce ilk dikkatimi çeken iki şifreleme sınıfınında metodlarının isim olarak(encrypt,decrypt ve encode,decode ) birbirinden faklı olması. Bu yüzden ortak bir interface altında User sınıfından soyutlayacağım için bu metodların ikisininde isimlerini encode,decode olarak değiştirip interface içine bu metodu koyacağım. Fazla uzatmadan kodun yeni haline bakalım.

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.KeySpec;


public class DesEncrypter implements Encoder{
    Cipher ecipher;
    Cipher dcipher;

    public DesEncrypter(String passPhrase) {
        byte[] salt = {(byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x34, (byte) 0xE3, (byte) 0x03};

        int iterationCount = 19;
        try {
            KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
            SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);

            ecipher = Cipher.getInstance(key.getAlgorithm());
            dcipher = Cipher.getInstance(key.getAlgorithm());
            AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);

            ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
            dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);

        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public String encode(String str) {
        try {
            byte[] utf8 = str.getBytes("UTF8");
            byte[] enc = ecipher.doFinal(utf8);
            return new sun.misc.BASE64Encoder().encode(enc);

        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public String decode(String str) {
        try {
            byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(str);
            byte[] utf8 = dcipher.doFinal(dec);
            return new String(utf8, "UTF8");

        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }
}
public class CaesarCipher implements Encoder {
    private int offset;
    public CaesarCipher(int offset) {
        this.offset = offset;
    }

    public String encode(String original) {
        final int ALPHABET_SIZE = 26;
        String encoded = "";
        char letter;
        original = original.toUpperCase();

        for (int index = 0; index < original.length(); index++) {
            letter = original.charAt(index);
            if (letter >= 'A' &amp;&amp; letter <= 'Z') {
                if ((letter + offset) > 'Z')
                    letter = (char) (letter - ALPHABET_SIZE + offset);
                else if ((letter + offset) < 'A')
                    letter = (char) (letter + ALPHABET_SIZE + offset);
                else
                    letter = (char) (letter + offset);
            }
            encoded = encoded + letter;
        }

        return encoded;
    }

    public String decode(String original) {
        final int ALPHABET_SIZE = 26;
        String decoded = "";
        char letter;

        original = original.toUpperCase();

        for (int index = 0; index < original.length(); index++) {
            letter = original.charAt(index);
            if (letter >= 'A' &amp;&amp; letter <= 'Z') {

                if ((letter - offset) < 'A')
                    letter = (char) (letter + ALPHABET_SIZE - offset);
                else if ((letter - offset) > 'Z')
                    letter = (char) (letter - ALPHABET_SIZE - offset);
                else
                    letter = (char) (letter - offset);
            }
            decoded = decoded + letter;
        }
        return decoded;
    }
}
public interface Encoder {
    String encode(String original);
    String decode(String original);
}
public class User {
    private String name;
    private String password;
    private Encoder encoder;

    public User(Encoder encoder) {
        this.encoder = encoder;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setPassword(String password) {
        this.password = encoder.encode(password);
        System.out.println("Password encoded : " + this.password);
    }

    public String getPassword() {
        return encoder.decode(this.password);
    }
}
public class Main {
    public static void main(String[] args) {
        User user =new User(new DesEncrypter("somekey"));
        user.setName("Cihat");
        user.setPassword("ABCDEF");

        System.out.println(user.getPassword());
    }
}

Ayrıca UML diyagramına tekrar bakalım.

Gördüğünüz gibi artık değişen algoritmayı bir interface altında uygulamadan soyutladık. Artık User sınıfımız algoritma sınıflarından habersiz .Değişime karşı esnek yapıda. Ayrıca encodeType değişkeninden de kurtulduk. Dikkat ederseniz Main sınıfı içinde artık uygulamamıza Encoder interface’ni uygulayan sınıf User sınıfının yapıcı metodu kullanılarak veriliyor. Buna Dependency Injection deniyor. Bunu bizim için yapan birçok framework mevcut(Spring,PicoContainer,Castle Windsor) bunlarıda ayrı bir yazı altında inceleriz.Şimdilik benden bu kadar.Sorularınız, sorunlarınız, bütün görüşlerinizi yazabilirsiniz bunları duymaktan memnun olurum.