26 Temmuz 2021 Pazartesi

GoF - Builder Örüntüsü

Giriş
Temelde iki çeşit Builder örüntüsü var
1. Blind Builder : Ayrı bir sınıftır
2. Bloch Builder : Kullanılmak istenen sınıfın içine public static bir builder yazılır. 
3. Functional Builder : Sınıfa bir şeyler eklemek için array of functions kullanılır

Blind Builder ve Bloch Builder'ın Ortak Özellikleri
Her iki Builder sınıfı da seçime bağlı özellikler için default değerler sağlamalıdır. Zorunlu parametreler zaten kullanıcı tarafından verilecektir.
class Builder {
  private String builderColor = "white"; // default color
  private String builderWheels = "round"; // default wheels
  private int builderSpeed = 100; // default speed
     
}
Build Edilen Sınıf Zorunlu Parametreleri final Yapmalıdır
Bu alanlar artık değişmeyecektir.
public class Car {

    public static Builder builder() {
        return new Builder();
    }

    // static inner class builder
    public static class Builder {
        ...
    }

    // final (immutable) fields.
    private final String color;
    private final String wheels;
    private final int speed;

    // private constructor, only the builder calls this....
    private Car(String color, String wheels, int speed) {
        this.color = color;
        this.wheels = wheels;
        this.speed = speed;
    }
  
}

1. Blind Builder
Builder yarattığı nesneden ayrıdır. Yaratılan nesne ise içine Builder alan private bir constructor veya setter metodları tanımlar. Örnek
class CarBuilder {
  private String wheels;
  private String color;
  private int speed;

  ...
}
Bu Builder tüm zorunlu ve isteğe bağlı parametreleri içinde biriktirir. Daha sonra build metodu ile yeni bir nesne yaratır. Dolayısıyla build metodu aşağıdakine benzer.
public Car build() {
  // validate your attributes
  Car car = new Car();
  // set all attributes in your car
  return car;
}
Eğer gerekirse build metodu içinde verinin doğruluğu da kontrol edilebilir.
public Car build(){
    // validate your values e.g.
    if (speed < 25) {
      throw new IllegalValueException("Car is too slow.");
    }
    // further validation
    Car car = new Car();
    car.setWheels(wheels);
    car.setColor(color);
    car.setSpeed(speed);
    return car;
}

2. Bloch Builder
Bloch Builder, Effective Java kitabında gösteriliyor. Kullanılmak istenen sınıfın içine public static bir builder yerleştiriliyor. Yaratılan nesne ise içine Builder alan private bir constructor tanımlar.

Yukarıdaki örnekte Builder static ve nested bir sınıf. Kullanırken Pizza sınıfını yarattığı kolayca okunabilir.
Pizza pizza = new Pizza.Builder(12)
                       .cheese(true)
                       .pepperoni(true)
                       .bacon(true)
                       .build();
Örnek - Generics Kullanan Block Builder
Açıklaması şöyle. Bir başka açıklama burada
Type parameters are not inherited from outer class to static nested class. 
Bu yüzden şöyle yapmak gerekir
LanguageMatcher.Builder<MyClass, YourClass> lm =
    new LanguageMatcher.Builder<MyClass, YourClass>();
Örnek
Ben şöyle yaptım
public class RemoteMapSourceParams<T, K, V> {
  private final String mapName;
  private final Predicate<K, V> predicate;
  private final Projection<? super Map.Entry<K, V>, ? extends T> projection;

  private RemoteMapSourceParams(Builder<T, K, V> builder) {
    this.mapName = builder.mapName;
    this.predicate = builder.predicate;
    this.projection = builder.projection;
  }

  // Getters
  public static <T, K, V> Builder<T, K, V> builder(String mapName) {
    return new Builder<>(mapName);
  }

  public static class Builder<T, K, V> {
    private final String mapName;
    private String dataConnectionName;
    private ClientConfig clientConfig;
    private Predicate<K, V> predicate;
    private Projection<? super Map.Entry<K, V>, ? extends T> projection;

    public Builder(String mapName) {
      Objects.requireNonNull(mapName, "mapName can not be null");
      this.mapName = mapName;
    }

    //Setters
    public RemoteMapSourceParams<T, K, V> build() {
      return new RemoteMapSourceParams<>(this);
    }
  }
}
3. Functional Builder
Örnek
Elimizde şöyle bir arayüz olsun
@FunctionalInterface
interface Coffee {
  List<String> ingredients();  
        
  static Coffee withSaltedCaramelFudge(Coffee coffee) {
    return () -> coffee.add("Salted Caramel Fudge");
  }

  default List<String> add(String item) {
    return new ArrayList<>(ingredients()) {{
      add(item);
    }};
  }
    
  static Coffee withSweetenedMilk(Coffee coffee) {
    return () -> coffee.add("Sweetened Milk");
  }

  static Coffee withDarkCookieCrumb(Coffee coffee) {
    return () -> coffee.add("Dark Cookie Crumb");
  }

  static Coffee withVanillaAlmondExtract(Coffee coffee) {
    return () -> coffee.add("Vanilla/Almond Extract");
  }
}
Builder olarak şu kodu kullanırız.
@SafeVarargs
static Coffee getCoffeeWithExtra(Coffee coffee, Function<Coffee, Coffee>... ingredients) {
  var reduced = Stream.of(ingredients)
    .reduce(Function.identity(), Function::andThen);
  return reduced.apply(coffee);
}
Bu kodu kullanmak için şöyle yaparız
record CoffeeCup(List<String> initialIngredient) implements Coffee {
  @Override
  public List<String> ingredients() {
    return initialIngredient;
  }
}

var ingredients = List.of("Tim Horton");
var coffeeCup = new CoffeeCup(ingredients);

var coffee = getCoffeeWithExtra(coffeeCup,
  Coffee::withDarkCookieCrumb,
  Coffee::withSaltedCaramelFudge,
  Coffee::withSweetenedMilk,
  Coffee::withVanillaAlmondExtract);

  System.out.println("Coffee with " + String.join(", ", coffee.ingredients()));
}


24 Temmuz 2021 Cumartesi

QueryDSL Kütüphanesi

Giriş
"Type Safe" SQL cümlesi yazmak için kullanılır. Açıklaması şöyle
QueryDSL is an external library that uses annotation processing to generate additional classes – called Q—types. These are copies of the JPA entities but introduce an API that allows us to create database queries.
Maven
Şu satırı dahil ederiz
<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-jpa</artifactId>
</dependency>
<dependency>
  <groupId>com.querydsl</groupId>
  <artifactId>querydsl-apt</artifactId>
</dependency>
QueryDSL kod ürettiği için şu satırı dahil ederiz. Böylece her bir @Entity sınıfı için QFoo şeklinde kod üretilir.
<plugin>
<groupId>com.mysema.maven</groupId> <artifactId>apt-maven-plugin</artifactId> <version>1.1.3</version> <executions> <execution> <goals> <goal>process</goal> </goals> <configuration> <outputDirectory>target/generated-sources/java</outputDirectory> <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor> </configuration> </execution> </executions> </plugin>
JPAQuery Sınıfı
select metodu
Örnek
Şöyle yaparız
entityManager.getTransaction().begin();   

List<User> users = new JPAQuery<User>(entityManager)  
        .select(QUser.user)  
        .from(QUser.user)  
        .where(QUser.user.email.endsWith("gmail.com")  
                .and(QUser.user.firstName.eq("Alice")))  
        .orderBy(QUser.user.lastName.asc())  
        .fetch();  

entityManager.getTransaction().commit();  
entityManager.close();  
JPAQueryFactory Sınıfı
constructor
Şöyle yaparız
@Configuration
class QueryDslConfiguration {

  @PersistenceContext
  private EntityManager em;

  @Bean
  public JPAQueryFactory jpaQueryFactory() {
    return new JPAQueryFactory(em);
  }
}
fetch metodu
Şöyle yaparız
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;


@Repository
@RequiredArgsConstructor
public class DefaultTeamQueryRepository {

  private final JPAQueryFactory queryFactory;

  public List<MemberResponse> findMembersByTeamId(long teamId) {
    QMember memberTable = new QMember("member");

    return queryFactory
      .select(new QMemberResponse(memberTable.id,memberTable.name))
      .from(memberTable)
      .where(memberTable.team.id.eq(teamId))
      .fetch();
  }
}
fetchFirst metdou
Şöyle yaparız
public abstract class BaseRepositoryImpl<T, ID> extends SimpleJpaRepository<T, ID>
implements BaseRepository<T, ID> {
  protected final QAuthor author = QAuthor.author;
  protected final QBook book = QBook.book;

  private final EntityManager em;
  protected final JPAQueryFactory queryFactory;

  public BaseRepositoryImpl(Class<T> domainClass, EntityManager em) {
    super(domainClass, em);
    this.em = em;
    this.queryFactory = new JPAQueryFactory(em);
  }
  ...
}

@Repository
public class AuthorRepositoryImpl extends BaseRepositoryImpl<Author, Long>
implements AuthorRepository {
  public AuthorRepositoryImpl(EntityManager em) {
    super(Author.class, em);
  }

  @Override
  public Optional<Author> findByEmailIgnoreCase(String email) {
    return Optional.ofNullable(
      queryFactory
        .select(author)
        .from(author)
        .where(author.email.equalsIgnoreCase(email))
        .fetchFirst()
    );
  }
}
fetchJoin metodu
N+1 Select Problem çözümü için kullanılır. JPQL karşılığı olarak "JOIN FETCH" kullanılır.
Örnek
Şöyle yaparız
import com.querydsl.jpa.impl.JPAQueryFactory;

public Shipment getById(final UUID id) {
  final QShipment shipment = QShipment.shipment; // generated
  final QItem item = QItem.item;                 // generated
  return (Shipment) new JPAQueryFactory(entityManager)
    .from(shipment) 
    .innerJoin(shipment.items, item)
.fetchJoin() //Items is lazy, the fetchJoin will load it
    .where(shipment.id.eq(id.toString())) 
    .fetchOne();
}
Örnek
Şöyle yaparız
@Override
public List<Author> findAllWithBooks() { return queryFactory .select(author) .distinct() .from(author) .innerJoin(author.books, book) .fetchJoin() .fetch(); }
offset metodu
Şöyle yaparız
public Page<MemberResponse> members(Pageable pageable) {
  QMember memberTable = new QMember("member");

  List<MemberResponse> members = queryFactory
    .select(new QMemberResponse(memberTable.id,memberTable.name))
    .from(memberTable)
    .offset(pageable.getOffset())
    .limit(pageable.getPageSize())
    .fetch();

    return PageableExecutionUtils.getPage(members, pageable,
      () -> countQuery().fetchOne());
  }

  private JPAQuery<Long> countQuery() {
    QMember memberTable = new QMember("member");
    return queryFactory
      .select(memberTable.count())
      .from(memberTable);
  }
}
Açıklaması şöyle
We can use the same Pageable from Spring Data. Note that PageableExecutionUtils.getPage() is used instead of new PageImpl(...). This is an optimization technique to reduce the number of count calls.

select metodu
Örnek
Elimizde şöyle bir kod olsun
@Data
@AllArgsConstructor
public class AuthorStatistic {
  private String authorName;
  private Long bookSize;
}
Şöyle yaparız
@Override
public List<AuthorStatistic> findAuthorStatistic() {
  return queryFactory
    .from(author)
    .innerJoin(author.books, book)
    .groupBy(author.fullName)
    .select(Projections.constructor(AuthorStatistic.class,
                        author.fullName,
                        book.count())
    )
    .fetch();
}
Örnek
Elimizde şöyle bir kod olsun
import com.querydsl.core.annotations.QueryProjection;

public record MemberResponse(long id, String name) {

    @QueryProjection
    public MemberResponse {
    }
}
Şöyle yaparız
public List<MemberResponse> findMembersByTeamId(long teamId) {
  QMember memberTable = new QMember("member");

  return queryFactory
    .select(new QMemberResponse(memberTable.id,memberTable.name)
    .from(memberTable)
    .where(memberTable.team.id.eq(teamId))
    .fetch();
}


17 Temmuz 2021 Cumartesi

Kotlin open Anahtar Kelimesi

Giriş
Kotlin'de sınıflar final kabul edilir. Eğer kalıtmak istersek ata sınıfa open kelimesi eklenir.

Örnek
Arayüzlerde open anahtar kelimesine gerek yoktur. Zaten kalıtım olacağı bellidir. Şöyle yaparız.
interface Shape{
  fun side(): Int
  fun computeArea(): Double
  fun computePerimeter(): Double
  fun addColor(): String
}

class Rectangle(var l: Int, var w: Int): Shape(){
  override fun side(): Int {
    return 4
  }

  override fun computeArea(): Double {
    return (w*l).toDouble()
  }

  override fun computePerimeter(): Double {
    return (2*(l+w)).toDouble()
  }

  override fun addColor(): String {
    return "red"
  }
}
Örnek
Sınıflarda ise şöyle yaparız
open class Shape{
  //body

class Rectangle: Shape(){
  //body
}

14 Temmuz 2021 Çarşamba

gRPC Server API NettyChannelBuilder Sınıfı

forTarget metodu
Şöyle yaparız
NettyChannelBuilder
  .forTarget(properties.connectorServiceUrl)
  .usePlaintext()
  // Enable keepalive, with a time a bit smaller
  // than the observed resets
  .keepAliveTime(150, TimeUnit.SECONDS)
  .build()

gRPC Server API ManagedChannelBuilder Sınıfı

Giriş
Şu satırı dahil ederiz
import io.grpc.ManagedChannelBuilder;
Açıklaması şöyle
A gRPC channel is backed by one or more sub-channels; each sub-channel being a connection. Having more connections helps when you have multiple servers (backends) so each connection goes to a different backend. If you have a single server (backend) you should ensure that creating multiple connections to the same server will actually help which would be the case if the server is not the bottleneck and is able to process high network throughput.
forAddress metodu
Örnek
Şöyle yaparız
 ManagedChannelBuilder.forAddress("127.0.0.1", 9091)
                      .executor(Executors.newCachedThreadPool())
                      .usePlaintext()
                      .build()
forTarget metodu
Şöyle yaparız
ManagedChannelBuilder.forTarget("grpc-server-service:80") 
  .defaultLoadBalancingPolicy("round_robin")
  .usePlaintext()
  .build();

Kotlin suspend Anahtar Kelimesi

Suspension Function
Açıklaması şöyle.
Suspending functions may suspend the execution of the current coroutine without blocking the current thread.
suspend vs Callback
Açıklaması  şöyle
Suspend functions are a direct replacement for callbacks — it is one of the basic premises behind structured concurrency. Callbacks make code hard to follow (ie. they make your code unstructured). The classic example of this is the pyramid of doom or callback hell. Coroutines (represented primarily as suspend functions in Kotlin) are designed to clean that kind of code up — callbacks are no longer required
suspend vs Flow
Açıklaması şöyle
When designing your methods and functions, start with a suspend function and if you get stuck, change it to a Flow.
 
Kotlin ve suspend Anahtar Kelimesi
Açıklaması şöyle.
You may find functions like kotlinx’s delay or Ktor’s HttpClient.post that need to wait for something or do intensive work before returning, and are marked with the suspend keyword.
Örnek
Şöyle yaparız
suspend fun delay(timeMillis: Long) {...}
suspend fun someNetworkCallReturningValue(): SomeType {
 ...
}
Suspend return Type
Açıklaması şöyle
The suspending world is nicely sequential
You have probably noticed that suspending functions don’t have special return types. They are really declared just like usual functions. We don’t need any wrapper like Java’s Future or JavaScript's Promise. This confirms that suspending functions are not asynchronous themselves (at least from the point of view of the caller), unlike JavaScript’s async functions, which return promises.

This is what makes asynchronous stuff easy to reason about in Kotlin. Inside a suspending function, calls to other suspending functions behave like normal function calls: we need to wait for the execution of the called function before getting the return value and executing the rest of the code.
Ancak google tarafından önerilen şey bir Result sınıfı tanımlamak. Böylece when ile kullanılabilir. Şöyle yaparız
sealed class Result<out R> {
  data class Success<out T>(val data: T) : Result<T>()
  data class Error(val exception: Exception) : Result<Nothing>()
}
when (result) {
  is Result.Success<LoginResponse> -> // Happy path
  else -> // Show error in UI
}

Coroutine builders
1. runBlocking metodu
Örnek
Şöyle yaparız
fun main() { 
  println("Hello,")
    
  // we create a coroutine running the provided suspending lambda
  // and block the main thread while waiting for the coroutine to finish its
//execution
  runBlocking {
    // now we are inside a coroutine
    delay(2000L) // suspends the current coroutine for 2 seconds
  }
    
  // will be executed after 2 seconds
  println("World!")
}
2. funCatching
Kendi Result sınıfını döner. Bizim artık Result sınıfı tanımlamamıza gerek kalmaz. Şöyle yaparız
val statusResult: Result<String> = runCatching {
  // function that may throw exception that needs to be handled
  repository.userStatusNetworkRequest(username)
}.onSuccess { status: String ->
  println("User status is: $status")
}.onFailure { error: Throwable ->
  println("Go network error: ${error.message}")
}
3. launch metodu - Fire and Forget
Örnek
Şöyle yaparız
fun main() { 
    GlobalScope.launch { // launch new coroutine in background and continue
        delay(1000L)
        println("World!")
    }
    println("Hello,") // main thread continues here immediately
    runBlocking {     // but this expression blocks the main thread
        delay(2000L)  // ... while we delay for 2 seconds to keep JVM alive
    } 
}
4. await metodu - Get Result
Örnek
Şöyle yaparız
fun main() {
    val deferredResult: Deferred<String> = GlobalScope.async {
        delay(1000L)
        "World!"
    }
    
    runBlocking {
        println("Hello, ${deferredResult.await()}")
    }
}