28 Haziran 2019 Cuma

Yazılım Mimarisi

Yazılım Mimarisi Nedir ?
Yazılım Mimarisi (Software Architecture) bir sistemin paçalarını ve aralarındaki ilişkiyi gösterir. Mevcut bir yazılımın mimarisini değiştirmek zor bir iştir.

Mimarisel Nasıl Ortaya Çıkar
Mimari bir anda ortaya çıkmaz. Genellikle artırımsal yinelemelerle hayat bulur. Çünkü insanlar bir seferde her şeyi göremezler. Açıklaması şöyle.
We call this emergent design.

There are a number of techniques that can be used to encourage emergent design. Agile is a common methodology of emergent design.
Mimarisel Örüntüler - Architectural Pattern
Bazı yazılım mimarileri tekrarlana tekrarlana, artık bir referans model haline gelebilir. Açıklaması şöyle
Architectural patterns are ways of capturing proven good design structures, so that they can be reused. Software architects have been looking for ways to capture and reuse the architectural knowledge that have proven successful in the past.

More specifically, an architectural pattern is a package of design decisions that is found repeatedly in practice, has well defined properties that can be reused and describes a class of architectures.

Developing an architecture can be seen as a process of selecting, tailoring, and combining patterns. The software architect must decide how to instantiate a pattern, how to make it fit with the specific context and the constraints of the problem.
Yazılım Mimarisi ve Ekip Yapısı
Burada ilginç bir konu var. Açıklaması şöyle. Çeşitli yazılım mimarilerine göre ekibi de düzenlemek gerekebilir. Çünkü Conway yasasına göre, yazılım ekiplerin iletişim yapısına göre şekillenecektir.
Several studies have confirmed the core message of Conway’s Law that “Any organization that designs a system ... will produce a design whose structure is a copy of the organization's communication structure.” There are many subtleties to this in practice, but it boils down to this: If the intercommunication between teams does not reflect the actual or intended communication between software components, the software will be difficult to build and operate.

You can use “Reverse Conway”—changing the team structure to match the required system architecture—together with techniques such as domain-driven design (DDD) and code forensics, to reshape team responsibilities to align with the software architecture you need to produce in order to clarify boundaries and improve the development and operation of your systems.
Örnek
Örneğin eğitmen + kırmızı öğrenciler + mavi öğrenciler ilişkisi olan 3 tane ekip olsun. Her öğrenci grubu sadece kendi aralarında iletişim kurabilsinler ve eğitmen ile iletişim kurabilsinler. Bu durumda -  - eğitmene ait ekranlar kırmızı + mavi tüm nesneleri gösterebilecek şekilde olacaktır. Eğitmen öğrencilerden gelen isteklere cevap veren ekranlara da sahip olacaktır
- öğrenci grupları sadece kendi renklerine ait nesneleri yöneteceklerdir. Eğer aralarında hiyerarşi varsa yine aynı şekilde nesneler belli rollere de atanabilir.
Bu örnekte bariz bir şekilde yazılım da aynı şekilde geliştirilecektir.

Örnek
Mesela micro service mimarisine uygun yazılım geliştirmek isteyelim. O zaman Reverse Conway yasası uyarınca, ekipleri daha küçük ve bağımsız olacak şekilde düzenlemek gerekir. Açıklaması şöyle
This is one reason why organizations like Amazon and Netflix work in small, independent teams. It enables API-first design and development. And is represented as the well-known microservices deathstar.

Bazı Mimarisel Örüntüler

Bazı Mimariler
-Client/Server
-Peer to Peer
-Event Driven Architecture (EDA)
-Pipe and Filter Architecture
-Layered Architecture veya N-Layered Architecture
-Onion Architecture
-Hexagonal Architecture
-DCI Architecture
-Microkernel Architecture
-Microservices Architecture yazısına taşıdım.
-Service Oriented Architecture - SOA yazısına taşıdım.
-Microservices Architecture yazısına taşıdım.
- Space-Based Architecture yazısına taşıdım.
 
Yazılım ve Veri
Veri (data) , yazılım yeniden inşa edilse bile halen yaşamaya devam edecek tek şey olacaktır. Bu yüzden, veri modelini dikkat etmek gerekir.

Yazılım Kalite Etmenleri (Software Quality Attributes)
Yazılım Mimarı, kalite etmenlerinin seçilmesine dikkat eder. Yazılım Kalite Etmenleri yazısına taşıdım 

27 Haziran 2019 Perşembe

Trie

Giriş
Açıklaması şöyle. Bu veri yapısını telefon rehberini belleğe sığdırmak için kullandım.
A trie, also called digital tree and sometimes radix tree or prefix tree (as they can be searched by prefixes), is a kind of search tree—an ordered tree data structure ...
Radix Tree yazısına bakabilirsiniz

Trie Kullanan Algoritmalar
Aho-Corasick algoritması "string searching" algoritmalarından bir tanesi. Trie kullanılarak çözülebilir.

Avatajları
Şöyledir.
The basics:
 *Predictable O(k) lookup time where k is the size of the key
 *Lookup can take less than k time if it's not there
 *Supports ordered traversal
 *No need for a hash function
 *Deletion is straightforward

New operations:
 *You can quickly look up prefixes of keys, enumerate all entries with a given prefix, etc.

Binary Trie
Trie binary olmak zorunda değil.
Örnek
3 tane kelime içeren (shy,raku,rio) trie şeklen şöyledir.
              ROOT
          /     |  
        s       r    
      /      /     \
     h       a      i
    /      /       /
   y      k       o
         /  
        u
Örnek
Şöyledir
      A
     / \
    B   C
   / \   \
  D   E   F
Trie Tanımlama
Trie tanımlamada önemli olan şey bir node'un kaç tane daha node saklayabileceğine karar vermek. Ayrıca nod'lara ilave bilgiler de eklenebilir. Burada imla düzeltmesi (spell autocorrect) için kullanılan bir Trie var.

Örnek
Şöyle yaparız. Burada Dictionary kullanıldığı için bellek israfı yok
public class Trie : Dictionary<char, Trie>
{
  public void Add(string value)
  {
    var c = String.IsNullOrEmpty(value) ? '\0' : value[0];
    if (!this.ContainsKey(c))
    {
      this[c] = new Trie();
    }
    if (c != '\0')
    {
      this[c].Add(value.Substring(1));
    }
  }
}
Örnek
Basit bir trie için şöyle yaparız
class Trie {

Node root;

public Trie() {
  root = new Node();

}

boolean isPresent(String s) {
    
}
Node'lar ise şöyledir. Burada İngilizce Trie olduğu için 26 düğüm olacağı varsayılmış.
class Node {

  boolean terminal;
  int outDegree;
  Node[] children;

public Node() {
    terminal = false;
    outDegree = 0;
    children = new Node[26];
}
Örnek
Basit bir trie için şöyle yaparız. Önce Letter sınıfını tanımlarız.
//Trie taken from: https://stackoverflow.com/a/6073004
public struct Letter
{
  public const string Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  public static implicit operator Letter(char c)
  {
    return new Letter() { Index = Chars.IndexOf(c) };
  }
  public int Index;
  public char ToChar()
  {
    return Chars[Index];
  }
  public override string ToString()
  {
    return Chars[Index].ToString();
  }
}
Trie sınıfı şöyledir. Burada alt düğümler için yine Dictionary kullanılmış.
public class Trie
{
  public class Node
  {
    public string Word;
    public bool IsTerminal { get { return Edges.Count == 0 && Word != null; } }
    public Dictionary<Letter, Node> Edges = new Dictionary<Letter, Node>();
  }

  public Node Root = new Node();
  ...
}
Constructor şöyledir.
public Trie(string[] words)
{
  for (int w = 0; w < words.Length; w++)
  {
    var word = words[w];
    var node = Root;
    for (int len = 1; len <= word.Length; len++)
    {
      var letter = word[len - 1];
      Node next;
      if (!node.Edges.TryGetValue(letter, out next))
      {
        next = new Node();

        node.Edges.Add(letter, next);
      }

      if (len == word.Length)
      {
        next.Word = word;
      }

      node = next;
    }
  }
}
Add metodu
Şöyle yaparız.
tring[] arr = ...;
trie.Add(arr);
findByPrefix metodu
Örnek
Şöyle yaparız.
t.findByPrefix("rabb");
Örnek
Şöyle yaparız.
string FindLongestWord(Trie.Node node, string input, int charIndex)
{
  var character = char.ToUpper(input[charIndex]);

  string longestWord = null;

  foreach (var edge in node.Edges)
  {
    if (edge.Key.ToChar() == character)
    {
      var foundWord = edge.Value.Word;

      if (!edge.Value.IsTerminal)
      {
        var longerWord = FindLongestWord(edge.Value, input, charIndex + 1);
        if (longerWord != null) foundWord = longerWord;
      }

      if (foundWord != null && (longestWord == null ||
          edge.Value.Word.Length > longestWord.Length))
      {
        longestWord = foundWord;
      }
    }
  }
  return longestWord;
}
findValidPrefixes metodu
Elimizde şöyle bir Trie olsun.
public class SimpleTrie {
  private static final int ALPHABET_COUNT = 26;

  class TrieNode {
    char value;
    TrieNode[] children;
    boolean isValidWord;

    TrieNode() {
      this(' ');
    }

    TrieNode(char value) {
      this.value = value;
      children = new TrieNode[ALPHABET_COUNT];
      isValidWord = false;
     }
  }

  private TrieNode root = new TrieNode();

  public void insert(String word) {
    TrieNode current = root;

    for (int i = 0; i < word.length(); i++) {
      char c = word.charAt(i);

      if (current.children[c - 'a'] == null) {
        current.children[c - 'a'] = new TrieNode(c);
      }

      current = current.children[c - 'a'];
    }

      current.isValidWord = true;
  }
}
Şöyle yaparız.
public List<String> findValidPrefixes(String word) {
  List<String> prefixes = new ArrayList<>();
  TrieNode current = root;

  StringBuilder traversedPrefix = new StringBuilder();

  for (int i = 0; i < word.length(); i++) {
    char c = word.charAt(i);

    if (current.children[c - 'a'] != null) {
      current = current.children[c - 'a'];
      traversedPrefix.append(c);

      if (current.isValidWord) {
        prefixes.add(traversedPrefix.toString());
      }
    }
  }

  return prefixes;
}
insert metodu
Şöyle yaparız.
Trie<String, String> t = new Trie<String, String>();
t.insert("pizza", "🍕");
t.insert("rabbit1", "🐇");
t.insert("rabbit2", "🐰");

t.findByPrefix("rabb");