Welcome to My Blog 👋

Java, Spring Framework, Microservices, Docker, Kubernetes, AWS and Others 🚀
Follow Me
Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts



Merhabalar, bu post altında Java 8 itibariyle gelen ve gelecek olan yenilikleri paylaşıyorum.


#Java18 Features:

  • UTF-8 by Default
  • Simple Web Server
  • Code Snippets in Java API Doc
  • Reimplement Core Reflection with Method Handles
  • Vector API
  • Internet-Address Resolution SPI
  • Foreign Function & Memory API
  • Pattern Matching for switch


#Java17 Features (Long Term Support) :

  • Restore Always-Strict Floating-Point Semantics
  • Enhanced Pseudo-Random Number Generators
  • New macOS Rendering Pipeline
  • macOS/AArch64 Port
  • Deprecate the Applet API for Removal
  • Strongly Encapsulate JDK Internals
  • Pattern Matching for switch


#Java16 Features:

  • Vector API
  • Enable C++14 Language Features
  • Migrate from Mercurial to Git
  • Migrate to GitHub
  • Concurrent Thread-Stack Processing
  • Unix-Domain Socket Channels
  • Alpine Linux Port
  • Elastic Metaspace
  • Windows/AArch64 Port


#Java15 Features

  • Sealed Classes and Interfaces
  • EdDSA Algorithm
  • Hidden Classes
  • Pattern Matching for instanceof
  • Removed Nashorn JavaScript Engine
  • Reimplement the Legacy DatagramSocket API
  • Records (Second Preview)
  • Text Blocks become a standard feature


#Java14 Features

  • Pattern Matching for instanceof
  • Text Blocks
  • Helpful NullPointerExceptions
  • Records
  • Switch Expressions
  • Packaging Tool
  • NUMA-Aware Memory Allocation for G1
  • JFR Event Streaming
  • Non-Volatile Mapped Byte Buffers
  • Remove the Pack200 Tools and API


#Java13 Features:

  • Text Blocks (Preview)
  • Switch Expressions Enhancements (Preview)
  • Reimplement the Legacy Socket API
  • Dynamic CDS Archive
  • ZGC: Uncommit Unused Memory
  • FileSystems.newFileSystem() Method
  • DOM and SAX Factories with Namespace Support


#Java12 Features:

  • Collectors.teeing() in Stream API
  • String API Changes
  • Files.mismatch(Path, Path)
  • Compact Number Formatting
  • Support for Unicode 11
  • Switch Expressions (Preview)


#Java 11 Features (Long Term Support):

  • HTTP Client API
  • Launch Single-File Programs Without Compilation
  • String API Changes
  • Collection.toArray(IntFunction)
  • Files.readString() and Files.writeString()
  • Optional.isEmpty()


#Java10 Features:

  • Local Variable Type Inference
  • Time-Based Release Versioning
  • Garbage-Collector Interface
  • Parallel Full GC for G1
  • Heap Allocation on Alternative Memory Devices
  • Consolidate the JDK Forest into a Single Repository


#Java9 Features:

  • Java platform module system
  • Interface Private Methods
  • HTTP 2 Client
  • JShell
  • Platform and JVM Logging
  • Process API Updates
  • Collection API Updates
  • Improvements in Stream API
  • Multi-release JAR Files
  • @Deprecated Tag Changes
  • Stack Walking


#Java8 Features (Long Term Support):

  • Lambda expression
  • Stream API
  • Functional interface
  • Default and static methods
  • Optional class
  • Nashorn – JavaScript runtime engine
  • Unsigned Integer Arithmetic
  • Repeating annotations
  • New Date and Time API


Kaynak : Turkey Java User Group

 

Merhabalar, bu post altında takip ettiğim Java bloglarını paylaşıyorum. Yeni takip ettiğim bloglar oldukça bu yazıyı güncelliyorum.

Merhabalar, bu yazımda Intellij IDEA kullananlar için faydalı olacağını düşündüğüm bir cheat sheet paylaşıyorum.



Kaynak: https://www.jrebel.com/resources/java-resources#Java-Cheat-Sheets 

Merhabalar, bu yazımda Regex yazarken yararlı olabileceğini düşündüğüm bir cheat sheet paylaşıyorum.



Kaynak: https://www.jrebel.com/resources/java-resources#Java-Cheat-Sheets 

Merhabalar, bu yazımda Java developer olanların veya olmak isteyenlerin nasıl bir yol izlemesiyle ilgili oluşturulan yararlı bir grafiği paylaşmak istiyorum.


 

Kaynak: https://github.com/devoxx/JavaRoadmap2022 

  Merhabalar, bu yazımda mikroservis mimarisinde kullanılan pattern'leri gösteren bir diagramı paylaşmak istedim



Kaynak: Igor De Souza

 Merhabalar, bu yazımda mikroservis mimarisine yeni başlayacak olanlara yol gösterebileceğini düşündüğüm bir diagramı paylaşmak istedim


Kaynak: GuidesJava



Parola doğrulamasında kullanılabilecek regex

^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\S+$).{8,}$
^                 # İfadenin başlangıcı(?=.*[0-9])       # En az bir rakam olmalıdır(?=.*[a-z])       # En az bir küçük harf olmalıdır(?=.*[A-Z])       # En az bir büyük harf olmalıdır(?=.*[@#$%^&+=])  # En az bir özel karakter olmalıdır(?=\S+$)          # Whitespace karakter olmamalıdır.{8,}             # En az 8 karakterden oluşmalıdır$                 # İfadenin bitişi


Hibernate ile çalışırken kodumuz içinde çalıştırılan sql ifadelerinin console'dan izlenmesi sağlanabilir. Bunun için hibernate'in bize sağladığı bir takım tanımlamalar vardır. Bu tanımlamaları hibernate.properties dosyası içine yapabiliriz.

Aşağıdaki tanımlama ile runtime'da çalıştırılan sql ifadeleri console'a yazdırılır.
hibernate.show_sql=true

Aşağıdaki tanımlama ile console'a yazdırılan sql ifadeleri daha okunaklı olması amacıyla formatlanır.
hibernate.format_sql=true

Aşağıdaki tanımlama ile console'a yazdırılan sql ifadelerinin hangi hibernate operasyonları tarafından gerçekleştirildiği bilgisi console'a yazdırılır.
hibernate.use_sql_comments=true






Nesneye dayalı programlamada gerçek dünya nesnelerinin yazılıma taşınması amaçlanır. Gerçek nesneler birbirini kullanan, birbirine bağımlı olan varlıklardır. Gerçek hayattan kumanda ve televizyon gibi bir örnek verilebilir. Gerçek dünya nesnelerini yazılıma taşıdığımızda da nesnelerimizin arasında bir bağ yani bağımlılık olduğunu görebiliriz. Bu durum yazılım sistemimizde istemediğimiz bir durumdur çünkü mesela televizyonda yaptığımız bir değişiklik kumandayı da etkileyeceğinden fazladan kod düzenlemesi ve test ihtiyacı doğurur. Bu yüzden solid prensiplerinden biri olan açıklık kapalılık prensibi kodun değişime kapalı ama genişletmeye açık olduğunu söyler. Sistemimizde bir değişiklik yapma ihtiyacı duyduğumuzda kodumuzu değiştirmemeli, yeni kodlar eklemeliyiz. Bu şekilde birbirine bağımlı olan parçaları daha az etkiler, problemleri en aza indiririz. Bunu yapmak için nesneye yönelimli programlamanın temellerinden olan polimorfizm'i kullanarak soyut bir şekilde kod yazmalıyız. Bunu yaptığımızda tek başına problemimizi çözmez. Gerekli unsurlardan biri de Dependency Injection'dur. Bu yöntem ile bağımlılıklarımızı dışarıdan enjekte edebiliriz ve bağımlılıklar konusunda esneklik kazanabiliriz. 
Örneğin bir yazdır sınıfımızın olduğunu düşünelim. Yazdırma işlemi bir yazıcıya da olabilir bir excel'e de olabilir bir pdf'e de olabilir. Kullanıcının ilk etapta bir excel dosyasına çıktı almak istediğini düşünelim. Belli bir süre sonra değişen ihtiyaçlar doğrultusunda kullanıcı bir de pdf dosyasına çıktı almak isteyebilir. Bu durumda bizim kodda herhangi bir değişiklik yapmadan sadece pdf çıktısı almak için gerekli kodları ekleyerek güncellememizi yapmamız gerekir. Burada işlemin pdf ile mi yoksa excel ile mi gerçekleşeceğini de kodda belirlemek ve somut bir bağımlılık yaratmak yerine dışarıdan enjekte ederek soyut bir şekilde belirlemeliyiz.

Dependency Injection'un Temel Amaçları
  • Bağımlılıkların somut bir şekilde belirlenmesi yerine dışarıdan soyut bir şekilde belirlenmesi
  • Kaynak kodda değişikliğe ve yeniden derlemeye ihtiyaç duyulmaması
  • Bakımın ve güncellemelerin daha kolay yapılabilmesi
  • Bağımlılıkların tek bir noktada tasarlanması ve yönetilmesi
  • Bağımlılıklardan dolayı oluşan hataların en aza indirilmesi



interface IYazdir {
    
    public void yazdir();
}

class Excel implements IYazdir {
    
    public void yazdir() {
        System.out.println("Excel ile yazdırıldı!");
    }
}

class PDF implements IYazdir {
    
    public void yazdir() {
        System.out.println("PDF ile yazdırıldı!");
    }
}

class AySonuRaporu{
    
    IYazdir yazdirma;
    
    public AySonuRaporu(IYazdir yazdirma) {
        this.yazdirma = yazdirma;
    }
    
    public void aySonuRaporuCiktiAl() {
        yazdirma.yazdir();
    }
}

public class Test{

     public static void main(String []args) throws Exception{
         
        //Configurasyon dosyasından belirlediğimiz tür dışarıdan alınır
        //Alınan tür nesneye enjekte edilir
        //Burada configurasyon dosyasından okuma işlemleri yapılabilir
        //Ancak bu işin kalıplaşmış frameworkleri vardır. Örn spring
        //Spring burada bizim için configurasyonda belirttiğimiz türü enjekte eder
        //Konfigurasyon dosyasında türü pdf veya excel olarak değiştirerek kodda herhangi bir değişiklik yapmadan güncelleme yapabiliriz.
        //Örneğin bir word tipi eklendiğinde Word sınıfı yazılır ve konfigurasyon dosyasında tip word seçilir
        //Hiç bir kod değişikliği yapılmadığı için çok az test ihtiyacı doğar
        AySonuRaporu aySonuRaporu = new AySonuRaporu(new PDF());
        aySonuRaporu.aySonuRaporuCiktiAl();
     }
}

PDF ile yazdırıldı!

Günlük hayatta ve yazılım sistemlerinde genellikle varlıklar alt varlıklardan oluşur. Örneğin bir ülke içinde illeri onlar ilçeleri onlar da köyleri barındırırlar. Ülkede yaşanan herhangi bir gelişme onun içerisinde bulunduğu diğer alt varlıkları da etkiler. Bu durum günlük hayatta olduğu gibi yazılım sistemlerinde de vardır. Örneğin bir masaüstü uygulamada window açtığınızda bu window'un içinde resim ve yazılar olmaktadır. Bunlar da image ve label view'ları gibi alt varlıklar olabilirler. Window'un boyutunu değiştirdiğimiz zaman içinde bulunduğu yazı ve resimlerinde aynı şekilde boyutunun değiştiğini gözlemleyebiliriz. Bu problem yazılım sistemlerinde sıklıkla karşılaşılan bir problemdir. İç içe bulunan varlıkların tümü için tek tek yapılan değişikliğin yansıtılması yanlış bir yöntemdir ve işin içinden çıkılamayacak problemler ortaya çıkarır. Bu problemlerin çözümü için sunulan kalıplaşmış yöntem Composite Pattern'ıdır. Bu patern ile iç içe bulunan nesnelerin tümüne istenilen değişiklik tek bir noktadan uygulanır.

Uygulama
  • Component arayüzü iç içe bulunacak varlıklar için bir arayüz oluşturur ve operation metodu ortak işlemler için kullanılacak metotları temsilen yazılmıştır. Örneğimizde genel ön yüz elemanlarının arayüzüdür.
  • Composite sınıfı içerisine alt varlıklar alabilecek sınıfları temsil eder. Örneğimizde window sınıfıdır.
  • Leaf sınıfı içerisine bir alt varlık eklenemeyecek olan sınıfları temsil eder. Örneğimizde image sınıfıdır.


import java.util.ArrayList;

abstract class Component {
    protected String name;
    protected Boolean isParent;
    protected Component parent;
    protected ArrayList children;
    
    protected Component(String name, Boolean isParent) {
        this.name = name;
        this.isParent = isParent;
        if(isParent) {
            children = new ArrayList<>();
        }
    }
    
    public void add(Component component) throws Exception {
        if(isParent) {
            children.add(component);
        } else {
            throw new Exception("Bu işlem desteklenmiyor");
        }
    }
    
    public void remove(Component component) throws Exception {
        if(isParent) {
            children.remove(component);
        } else {
            throw new Exception("Bu işlem desteklenmiyor");
        }
    }
    
    public abstract void resize();
}

class Panel extends Component {
    
    public Panel(String name) {
        super(name, Boolean.TRUE);
    }
    
    public void resize() {
        System.out.println(name + " panelinin boyutu değişti!");
        if(!children.isEmpty()) {
            for(Component component : children) {
                component.resize();
            }
        }
    }
}

class ImageView extends Component {
    
    public ImageView(String name) {
        super(name, Boolean.FALSE);
    }
    
    public void resize() {
        System.out.println(name + " resminin boyutu değişti!");
    }
}

public class HelloWorld{

     public static void main(String []args) throws Exception{
        Panel panel = new Panel("Panel1");
        ImageView image1 = new ImageView("Image1");
        ImageView image2 = new ImageView("Image2");
        panel.add(image1);
        panel.add(image2);
        panel.resize();
     }
}

Panel1 panelinin boyutu değişti!
Image1 resminin boyutu değişti!
Image2 resminin boyutu değişti!

Facade Design Pattern'ının temel amacı soyutlama yapmaktır. Nesneye yönelimli programlamanın temellerinden biri soyutlamadır ve nesneye yönelimli programlamada daima kullanılır. Facade patern'ı sistemleri veya katmanları soyutlayarak bunların karmaşıklığını gizler, bir arayüz oluşturur, daha sonra kodların değiştirilebilirliğini sağlar ve doğrudan erişilmemesi gereken alanların kontrollü bir şekilde kullanılmasını sağlar. Örneğin yazılım sistemimizde veritabanı katmanını facade patern'ı ile soyutlayarak kodumuzu veritabanı sisteminden bağımsız yazabiliriz. Bir başka örnek olarak kullanılan kütüphaneler facade patern'ı ile bir arayüz şeklinde kullanıma açılarak o kütüphaneyi kullanacak kullanıcıya kolaylık ve doğru bir yol sağlanabilir. Bu verdiğim örnekler sıkça kullanılan ve genellikle karşınıza çıkacak örneklerdir.
Facade patern'ının en çok karşımıza çıktığı bir başka yer de framework'lerdir. Framework'lerin altında çalışan kütüphanelere veya alt sistemlere genellikle o framework'ün context sınıfı ile erişiriz. Bu context sınıfları genellikle facade patern'ı ile oluşturulur. Android'teki Context sınıfı veya Spring'teki ApplicationContext sınıfı örnek olarak verilebilir.

Uygulama
  • Facade sınıfı kullanacağı alt birimleri kendi içerisinde barındırır.
  • Facade sınıfı genellikle singleton pattern'ı ile tanımlanır.
  • Facade sınıfının alt birimleri factory metotlar ile dışarıya açılabilir.



class Facade {
    
    private AltSistem1 altSistem1;
    private AltSistem2 altSistem2;
    private static Facade facade;
    
    private Facade() {
        altSistem1 = new AltSistem1();
        altSistem2 = new AltSistem2();
    }
    
    public static Facade getFacade() {
        if(facade == null) {
            facade = new Facade();
        }
        return facade;
    }
    
    public void altSistem1IslemleriYap() {
        altSistem1.islem1();
        altSistem1.islem2();
    }
    
    public void altSistem2Islem1Yap() {
        altSistem2.islem1();
    }
}

class AltSistem1 {
    
    public void islem1() {
        System.out.println("Alt Sistem 1 İslem 1 yapıldı");
    }
    
    public void islem2() {
        System.out.println("Alt Sistem 1 İslem 2 yapıldı");
    }
}

class AltSistem2 {
    
    public void islem1() {
        System.out.println("Alt Sistem 2 İslem 1 yapıldı");
    }
}

public class Test {

     public static void main(String []args){
        Facade facade = Facade.getFacade();
        facade.altSistem1IslemleriYap();
        facade.altSistem2Islem1Yap();
     }
}

Alt Sistem 1 İslem 1 yapıldı
Alt Sistem 1 İslem 2 yapıldı
Alt Sistem 2 İslem 1 yapıldı

Adapter Design Pattern'ı adından da anlaşılacağı gibi iki uyumsuz nesnenin uyumlu bir şekilde çalışmasını sağlar ve aradaki uyumsuzluğu çözer.  Bu iki nesne farklı sistemlerin nesnesi de olabilir, aynı sistemdeki farklı çalışan iki nesne de olabilir. Örneğin elimizde bir model nesnesi olduğunu düşünelim. Bu model nesnesi kişilerin bilgilerini tuttuğu model olabilir. Bu kişileri bir liste halinde göstermek istersek listenin anlayacağı şekilde bu nesnedeki bilgileri düzenlememiz gerekir. Bu problemi adapter nesnesi çözer ve aldığı model sınıfını listenin anlayacağı şekilde adapte ederek uygun nesneyi oluşturur.

Uygulama
  • Adapter arayüzü ve bu arayüzü implement eden adapter sınıflarını tanımlarız. Bu adapter sınıfları iki sınıf arasındaki farklılıkları çözen sınıflardır. İhtiyacımız doğrultusunda istediğimiz kadar tanımlarız.
  • Adapter sınıfları içinde adapte edecekleri nesneleri barındırırlar.
  • Client sınıfları adapter arayüzü ile iki nesneyi adapte edebilirler.




import java.util.ArrayList;
import java.util.List;

class Insan {
    String ad;
    String soyad;
    
    public Insan(String ad, String soyad) {
        this.ad = ad;
        this.soyad = soyad;
    }
    
    public String getAd() {
        return ad;
    }
    
    public String getSoyad() {
        return soyad;
    }
}

class ListView {
    List<ListViewModel> listViewModelList;
    
    public ListView(List<ListViewModel> listViewModelList) {
        this.listViewModelList = listViewModelList;
    }
    
    public void goster() {
        for(ListViewModel model : listViewModelList) {
            System.out.println("Baslik : " + model.getBaslik());
            System.out.println("Aciklama : " + model.getAciklama());
        }
    }
}

class ListViewModel {
    String baslik;
    String aciklama;
    
    public ListViewModel(String baslik, String aciklama) {
        this.baslik = baslik;
        this.aciklama = aciklama;
    }
    
    public String getBaslik() {
        return baslik;
    }
    
    public String getAciklama() {
        return aciklama;
    }
}

interface Adapter {
    public void get();
}

class ListViewInsanAdapter implements Adapter {
    List<Insan> insanList;
    
    public ListViewInsanAdapter(List<Insan> insanList) {
        this.insanList = insanList;
    }
    
    public List<ListViewModel> get() {
        List<ListViewModel> listViewModelList = new ArrayList<>();
        for(Insan insan : insanList) {
            ListViewModel model = new ListViewModel(insan.getAd(), insan.getSoyad());
            listViewModelList.add(model);
        }
        return listViewModelList;
    }
}

public class Test {

     public static void main(String []args){
        Insan insan = new Insan("Berkay","Demirel");
        Insan insan2 = new Insan("Deneme","Insan");
        List<Insan> insanList = new ArrayList<>();
        insanList.add(insan);
        insanList.add(insan2);
        Adapter adapter = new ListViewInsanAdapter(insanList);
        ListView listView = new ListView(adapter.get());
        listView.goster();
     }
}

Baslik : Berkay
Aciklama : Demirel
Baslik : Deneme
Aciklama : Insan

Yazılım sistemimizi ihtiyaçlarımız doğrultusunda sınıflara bölerek tasarlarız ve bu sınıflara istediğimiz özellikleri veririz. Ancak bazı durumlarda sınıflardan ürettiğimiz nesnelerin farklı davranışlar sergilemesini isteyebiliriz. Bu probleme çözüm getiren kalıp Decorator Pattern'ıdır. Burada dikkat edilmesi gereken usun bu işlem çalışma zamanında yapılır ve sınıfta bir değişiklik yapılmaz. Sadece istenilen nesne üzerinde değişiklik yapılır ve o sınıftan üretilmiş diğer nesneler bu değişiklikten etkilenmezler. Örnek verecek olursak bir şekil arayüzümüzün olduğunu ve bu arayüzü kare, daire gibi sınıfların implement ettiğini düşünelim. Eğer kullanıcının seçtiği bir nesnenin renklendirilmesini istiyorsak o zaman sadece o nesneye dışarıdan ek özellik olarak bu durumu ekleyerek çizdirdiği şekli renkli hale getirebiliriz.

Uygulama
  • Component sınıfı ek özellik ekleyeceğimiz sınıfın arayüzü olmalıdır. Örneğimizde bu şekil arayüzüne denk gelmektedir.
  • ConcreteComponent sınıfı component arayüzünü implement eden ek özellik ekleyeceğimiz sınıflardır. Örneğimizde kare, daire gibi sınıflara denk gelmektedir.
  • Decorator sınıfımız bu patern'daki işlemleri yerine getirecek saf sanal sınıfımızdır. Bu sınıf ek özellik ekleyeceğimiz sınıfların arayüzünü implement etmeli ve ek özellik eklenecek nesneleri içermelidir.
  • ConcreteDecorator sınıfımız ek özellik ekleyeceğimiz nesnemize ekleyeceğimiz özellikleri belirttiğimiz sınıfımızdır.




interface Sekil {
    
    public void ciz();
}

class Kare implements Sekil {
    
    public void ciz() {
        System.out.println("Kare çizildi");
    }
}

class Daire implements Sekil {
    
    public void ciz() {
        System.out.println("Daire çizildi");
    }
}

abstract class SekilDecorator implements Sekil {
    protected Sekil decoratedSekil;
    
    public SekilDecorator(Sekil decoratedSekil) {
        super();
        this.decoratedSekil = decoratedSekil;
    }
} 

class BoyaDecorator extends SekilDecorator {
    protected String renk;
    
    public BoyaDecorator(Sekil decoratedSekil, String renk) {
        super(decoratedSekil);
        this.renk = renk;
    }
    
    public void ciz() {
        System.out.println(renk + " boyalı");
        decoratedSekil.ciz();
    }
}

public class Test {

     public static void main(String []args){
        Sekil kare = new Kare();
        kare.ciz();
        Sekil decoratedKare = new BoyaDecorator(new Kare(), "kırmızı");
        decoratedKare.ciz();
     }
}

Kare çizildi
kırmızı boyalı
Kare çizildi

Bilgisayar sistemlerinde geçmişi saklamak bir zorunluluktur. Örneğin kullanıcı işlemlerini yaparken muhtemel olarak bir hata yapacaktır ve yaptığı hatayı geri almak isteyecektir. Yazılım sistemimizde bu olaya çözüm bulmak için nesnelerimizin geçmişini saklamak zorunda kalabiliriz. Bu duruma çözüm getiren kalıp Memento Pattern'ıdır. Bu patern ile nesnemizin bir veya daha fazla geçmiş durumunu saklayarak istenilen zamanlarda bu geçmiş durumları nesneye tekrar yükleyebiliriz.

Uygulama
  • Bu yöntemi kullanacak client sınıflara bir arayüz görevi görecek Originator sınıfımızı yaratırız. Bu sınıf memento nesnelerinin yani geçmiş durumu tutacak nesneleri yönetmekten sorumludur.
  • Nesnemizin geçmiş durumunu saklaması için Memento sınıfını oluştururuz.
  • Birden fazla geçmiş saklamak istiyorsak Caretaker sınıfını oluştururuz. Saklayacağımız memento nesnelerini bu sınıf barındırır.



public class Memento {
     public String durum;
}

public class Originator {
     public String durum;
     
     public Memento save() {
         return new Memento(durum);
     }
     
     public String get(Memento memento) {
         return memento.getDurum();
     }
}

public class Caretaker {
    
    final Deque<EmpMemento> mementos = new ArrayDeque<>();
    public Memento getMemento()
    {
        return mementos.pop();
    }
    public void setMemento(Memento memento)
    {
        mementos.push(memento);
    }
}

public class TEST {
    
    public static void main() {
        Originator originator = new Originator("İlk durumum");
        Caretaker caretaker = new Caretaker();
        caretaker.setMemento(originator.save());
        originator.setDurum("İkinci durumum");
        caretaker.setMemento(originator.save());
        String sonDurum = originator.get(caretaker.getMemento);
    }
}

Eğer elimizde yorumlanması gereken bir ifade var ise bu iş için tanımlanmış kalıp Interpreter Design Pattern'ıdır. Bu patern ile yorumlanması gereken ifadeler birden farklı yöntem ile yorumlanması gerekebilir. Örneğin elimizde xml ve json formatında dosyalar olduğunu düşünelim. Bu dosyaların metin olarak ele alınıp uygun formata göre işlenmesi ve anlamlandırılması gerekmektedir. Bu kalıp ile bu probleme çözüm sunulmuştur.

Uygulama
  • Context sınıfı bize içeriği yani yorumlanması gereken ifadeyi sağlayan sınıftır.
  • Client sınıfı içeriği yorumlayarak işlem yapmak isteyen sınıftır.
  • AbstractExpression sınıfı ifadeyi yorumlamak için client sınıflarına sağlanan bir arayüzdür. İfadeyi yorumlayacak sınıflar bu arayüzü implement ederek yöntemlerini uygularlar.
  • TerminalExpression ile NonterminalExpression sınıfları arasındaki fark örneğin bir xml elementini yorumlarken bir tag içerisinde bir değer var ise o değeri alıp geriye dönebiliriz. Bu ifade bir terminalExpression'dır. Ancak eğer bir tag içinde bir veya daha fazla tag varsa o tag'in içindeki ifade tekrar yorumlanmak üzere nonterminalExpression sınıfına gönderilmelidir. Bu şekilde içerisindeki değerler döndürülünceye kadar ifade işlenmelidir.



public interface XMLAbstractExpression {

     public String interpret(Context context);
}

public class XmlTerminalExpression implements XMLAbstractExpression {
    
    public String interpret(Context context) {
        //Eğer tag'in içerisinde başka bir tag yoksa ifadeyi döndür
        //Eğer tag'in içerisinde başka bir tag varsa XmlNonterminalExpression'a gönder
    }
}

public class XmlNonterminalExpression implements XMLAbstractExpression {
    
    public String interpret(Context context) {
        //Eğer tag'in içerisinde başka bir tag yoksa ifadeyi XmlTerminalExpression'a gönder
        //Eğer tag'in içerisinde başka bir tag varsa ifadeyi tekrar XmlNonterminalExpression'a gönder
    }
}

public class Context {
    String expression;
}

public class TEST {
    
    public static void main() {
        XMLAbstractExpression xmlExpressionManager = new XmlTerminalExpression();
        Context context = new Context();
        context.setExpression("<note>
                                <to>Tove</to>
                                <from>Jani</from>
                                <heading>Reminder</heading>
                                <body>Don't forget me this weekend!</body>
                                </note>");
        xmlExpressionManager.interpret(context);
    }
}

Yazılımımızda aynı işi duruma göre farklı şekilde yapmak istersek kullanabileceğimiz yöntemlerden biri Template Method Pattern'ıdır. Bu patern uygulamamızda yapılacak bir işin çeşitli nedenlerden dolayı iki ya da daha fazla farklı yöntem ile yapılması gerekiyor ise bize uygun kalıbı sağlar. Örneğin elimizdeki bir metni farklı formatlarda göstermek isteyebiliriz. Kullanıcı pdf veya word dosyası olarak bu metni görüntülemek veya dışarı aktarmak isteyebilir. Bu durumda bu işleri birbirinden bağımsız şekillerde tasarlamak ileride problemlere yol açacaktır. Bu yüzden bu yöntemlerin tümünü aynı çatı altında tasarlayarak bir arayüz vasıtasıyla bu yöntemleri kullanacak sınıflara açmalıyız.

Uygulama
  • Yöntemler için ortak bir arayüz tanımlanır. Bu arayüz saf sanal bir sınıf olur. Bu sınıfın dışarıya açılan bir template metodu olur. Dışarıdan bu yöntemleri kullanacak sınıflar template metot üzerinden erişim sağlarlar. Template metot ile uygun yöntem çalıştırılır.
  • Her yöntem için bir sınıf tanımlanır. Bu sınıf ortak arayüz olan saf sanal sınıftan türetme yapılarak oluşturulur. Override edilen metoda uygun yöntem tanımlanır.





public abstract class DokumanYoneticisi {

     public void goruntule() {
         
         //ortak işlemler
         render();
     }
     
     public abstract void render();
}

public class PDF extends DokumanYoneticisi {
    
    public void render() {
        System.out.println("PDF görüntülendi");
    }
}

public class Word extends DokumanYoneticisi {
    
    public void render() {
        System.out.println("Word görüntülendi");
    }
}

public class TEST {
    
    public static void main() {
        DokumanYoneticisi dokumanYoneticisi = new PDF();
        dokumanYoneticisi.goruntule();
    }
}

Eğer sistemimizde içinde bulunduğu duruma göre farklı davranış sergilemesi gereken bir sınıf var ise State Pattern'ını kullanmalıyız. Bu patern bulunduğu duruma göre farklı hareket eden nesneler için bir kalıp sunar. Örneğin bir kaset çalar için çalma durumunda göstereceği davranış ile durma durumunda göstereceği davranış farklıdır. Bu kaset çalar çalma durumundayken çal tuşuna basıldığında bir davranış sergilemezken durma durumunda olduğunda çal tuşuna basıldığında bir davranış sergiler. Bu gibi nesneleri modellerken bu State Pattern'ı kullanılabilir.

Uygulama
  • Nesnenin her bir durumunda farklı davranış sergileyeceği sınıflar oluşturulur.
  • Bu sınıflar ortak bir arayüzü implement ederler.
  • İstemci sadece Context sınıfını bilerek yapmak istediği işlemi uygular. Context sınıfı State arayüzünü kullanarak işlemleri uygular.





public interface IKasetCalarState {
    
    public void cal();
    public void durdur();
}

public class KasetCalarContext {
    IKasetCalarState state;
    
    public KasetCalarContext() {
        IKasetCalarState state = new DuruyorState();
    }
    
    public void cal() {
        state.cal();
    }
    
    public void durdur() {
        state.durdur();
    }
}

public class CaliyorState implements IKasetCalarState {
    
    public void cal() {
        System.out.println("Kaset zaten çalınıyor");
    }
    
    public void durdur() {
        System.out.println("Kaset durduruldu");
    }
}

public class DuruyorState implements IKasetCalarState {
    
    public void cal() {
        System.out.println("Kaset çalınmaya başlandı");
    }
    
    public void durdur() {
        System.out.println("Kaset zaten çalınmıyor");
    }
}

public class Test {
    
    public static void main() {
        KasetCalarContext context = new KasetCalarContext();
        context.cal();
        context.durdur();
    }
}