Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Design patterns

Buzzvil
August 29, 2018

Design patterns

By Ben

Buzzvil

August 29, 2018
Tweet

More Decks by Buzzvil

Other Decks in Programming

Transcript

  1. Singleton pattern When to Use Singleton pattern should be used

    when we must ensure that only one instance of a class is created and when the instance must be available through all the code. A special care should be taken in multi-threading environments when multiple threads must access the same resources through the same singleton object. Common Usage • Logger Classes • Configuration Classes • Accessing resources in shared mode • Other design patterns implemented as Singletons: Factories and Abstract Factories, Builder, Prototype
  2. Singleton pattern Buzzscreen go - Mysql Lazy loading • Early

    vs Lazy Keep in mind • Synchronized • Initialize members when the instance is initialized • Most members shouldn’t have a state • Shouldn’t pass a parameter can have a state (like context) func getEsInstance() *SingletonElasticsearch { onceElasticsearch.Do(func() { instanceElasticsearch = &SingletonElasticsearch{} core.Logger.Infof("getEsInstance() - %s", Config.ElasticSearch.Host) var err error logger := ElasticInfoLogger{} httpClient := &http.Client{ Timeout: time.Second * 3, } optionsFuncs := []elastic.ClientOptionFunc{ elastic.SetURL(Config.ElasticSearch.Host), elastic.SetHealthcheckTimeout(10 * time.Second), elastic.SetSniff(false), elastic.SetErrorLog(logger), elastic.SetHttpClient(httpClient), } instanceElasticsearch.Elasticsearch, err = elastic.NewClient(optionsFuncs...) ... }) return instanceElasticsearch }
  3. Factory pattern When to Use Factory pattern should be used

    when: • a framework delegate the creation of objects derived from a common superclass to the factory • we need flexibility in adding new types of objects that must be created by the class Common Usage Along with singleton pattern the factory is one of the most used patterns. Almost any application has some factories. Here are a some examples in java: • factories providing an xml parser: javax.xml.parsers.DocumentBuilderFactory or javax.xml.parsers.SAXParserFactory • java.net.URLConnection - allows users to decide which protocol to use
  4. Factory pattern Buzzscreen core - Campaign presenter Keep in mind

    • Return type can be a super class or an interface • Don’t pass an argument which is related to return type • If possible, define the presenter constructor as protected or default - Why? public class CampaignPresenterFactory { public static CampaignPresenter<? extends Creative> getPresenter(Context context, Campaign campaign) throws UnsupportedCampaignException { CampaignPresenter<? extends Creative> presenter; switch (campaign.getCreative().getType()) { default: case IMAGE: presenter = new NativeCampaignPresenter(context, campaign, true); break; case NATIVE: presenter = new NativeCampaignPresenter(context, campaign); break; case SDK: presenter = new SdkCampaignPresenter(context, campaign); break; case WEB: case JS: presenter = new WebCampaignPresenter(context, campaign); break; } return presenter; } }
  5. Builder pattern When to Use Builder pattern should be used

    when: • the creation algorithm of a complex object is independent from the parts that actually compose the object • the system needs to allow different representations for the objects that are being built Example • Java ◦ CaptchaResult.java ◦ Usage • Go ◦ EsQueryBuilder.go ◦ Usage
  6. Builder pattern Buzzscreen core - HttpRequest Keep in mind •

    Members shouldn’t be public • Don’t provide setter method in the main class • Return builder itself in order to be a chain public class HttpRequest { private static final String TAG = HttpRequest.class.getSimpleName(); @Expose protected String url; protected String waitString; … protected HttpRequest(Context context) { this.context = context; } public static class Builder { protected HttpRequest httpTask; public Builder(Context context) { httpTask = new HttpRequest(context); if (queue == null) { queue = Volley.newRequestQueue(context); queue.start(); } } public Builder addParam(String key, Object value) { httpTask.params.put(key, String.valueOf(value)); return this; } public Builder setUrl(String url) { httpTask.url = url; return this; } public HttpRequest build() { return httpTask; } } }
  7. Observer pattern When to Use The observer pattern is used

    when: • the change of a state in one object must be reflected in another object without keeping the objects tight coupled. • the framework we are writing needs to be enhanced in future with new observers with minimal changes. Common Usage • Model View Controller Pattern - The observer pattern is used in the model view controller (MVC) architectural pattern. In MVC the this pattern is used to decouple the model from the view. View represents the Observer and the model is the Observable object. • Event management - This is one of the domains where the Observer patterns is extensively used. Swing and .Net are extensively using the Observer pattern for implementing the events mechanism.
  8. Observer pattern https://stackoverflow.com/questions/ 33486006/should-we-use-weakreferen ce-in-observer-pattern Keep in mind • Use

    WeakReference if possible • I think map is better than list for observer holders • Always keep in mind that you should write an unregister code right after when you write a register code
  9. Adapter pattern When to Use The visitor pattern is used

    when: • When you have a class(Target) that invokes methods defined in an interface and you have a another class(Adapter) that doesn't implement the interface but implements the operations that should be invoked from the first class through the interface. You can change none of the existing code. The adapter will implement the interface and will be the bridge between the 2 classes. • When you write a class (Target) for a generic use relying on some general interfaces and you have some implemented classes, not implementing the interface, that needs to be invoked by the Target class. Common Usage • Non Software Examples of Adapter Patterns : Power Supply Adapters, card readers and adapters, ... • Software Examples of Adapter Patterns: Wrappers used to adopt 3rd parties libraries and frameworks - most of the applications using third party libraries use adapters as a middle layer between the application and the 3rd party library to decouple the application from the library. If another library has to be used only an adapter for the new library is required without having to change the application code.
  10. Adapter pattern https://github.com/Buzzvil/slidejoy-android/bl ob/63265157d3458b35ce3320c1d366994f7f9 3edc7/app/src/main/java/com/slidejoy/contr ol/HeaderRecyclerAdapter.java#L11 Keep in mind •

    Consider any target class can work with your adapter public abstract class HeaderRecyclerAdapter<VH extends RecyclerView.ViewHolder, H extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<RecyclerView.ViewHolder> { protected static final int TYPE_HEADER = -2; protected static final int TYPE_ITEM = -1; RecyclerHeader<H> recyclerHeader; public void setRecyclerHeader(RecyclerHeader<H> recyclerHeader) { this.recyclerHeader = recyclerHeader; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { RecyclerView.ViewHolder viewHolder; if (isHeaderType(viewType)) { viewHolder = recyclerHeader.onCreateHeaderHolder(parent, viewType); } else { viewHolder = onCreateItemHolder(parent, viewType); } return viewHolder; } protected abstract int getCount(); protected abstract VH onCreateItemHolder(ViewGroup parent, int viewType); protected abstract void onBindItemHolder(VH holder, int position); public interface RecyclerHeader<H> { H onCreateHeaderHolder(ViewGroup parent, int viewType); void onBindHeaderHolder(H holder, int position); } }
  11. MVC pattern (Buzzscreen go) Model • Data used by business

    logic • DAO - DBMS related • Light model is preferred View • Presentation logic Controller • Select a business logic to process data • Return the result to the view
  12. MVC - Model https://github.com/Buzzvil/slidejoy-appserver /blob/master/src/main/java/com/slidejoy/ser ver/model/UserTransaction.java Keep in mind •

    DON’T include any business logic • SHOULDN’T import controller or service packages (package level cycling dependency) @Entity @Table(name = "tbl_userTransactionBS") @Accessors(chain = true) @ToString public class UserTransaction { public enum Status { Withdraw(1), Submit(2), Issue(3), Fail(4), Refund(5), Cancel(6); final int num; Status(int num) { this.num = num; } public int get() { return num; } } @Id @GeneratedValue(strategy = GenerationType.AUTO) @Getter long transactionId; @Getter @Setter String userId, account, storeName, productName, note; @Getter @Setter int productId, carats, status, orderId; }
  13. MVC - DTO https://github.com/Buzzvil/buzzscreen/blob/d 177d430e30aafbd42e7ce0c1205c84ff0496c2 8/buzzscreen-api/buzzscreen/dto/allocation_ v1.go Keep in mind

    • Data transfer object also doesn’t have any business logic • Model classes shouldn’t import dto classes • DTO & Model ◦ Inheritance.. package dto type ( AllocV1Request struct { ContentAllocV1Request IsTest bool `form:"is_test"` UserAgentReq string `form:"user_agent"` SettingsPageDisplayRatio string `form:"settings_page_display_ratio"` DefaultBrowser string `form:"default_browser"` InstalledBrowsers string `form:"installed_browsers"` IsIfaLimitAdTrackingEnabled bool `form:"is_ifa_limit_ad_tracking_enabled"` userAgent string } AllocV1Settings struct { AdFilteringWords *string `json:"ad_filtering_words"` BaseHourLimit int `json:"base_hour_limit"` BaseInitPeriod int `json:"base_init_period"` ExternalBaseReward int `json:"external_base_reward"` ... } )
  14. MVC - Controller Keep in mind • If there is

    a logic doesn’t strongly related with API, you should consider to implement the logic in service layer. Unless, you will have a moster controller. (And you might have many duplicated or similar code) buzzscreen/controller/device_v1.go func InitDeviceV1(c *gin.Context) { var deviceReq dto.InitDeviceV1Request err := bindValue(c, &deviceReq) ... device, _ := service.HandleNewDevice(&requestedDevice, c.Request) ... writeJsonSafely(c, http.StatusOK, res) } buzzscreen/controller/device.go func PostDevice(c *gin.Context) { var deviceReq dto.DeviceRequest err := bindValue(c, &deviceReq) ... device, isNewDevice := service.HandleNewDevice(&requestedDevice, c.Request) ... writeJsonSafely(c, http.StatusOK, res) }
  15. MVC - Service https://github.com/Buzzvil/slidejoy-appserver/blob /935cc0288ec34be92fd319ac9bcb71f9bc9e7900/s rc/main/java/com/slidejoy/server/service/StoreSer vice.java Controller vs Service

    If we are running a restaurant.. • Controller - Serving food • Service - Kitchen @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection") @Service public class StoreService { @Autowired private UserTransactionDao userTransactionDao; @Autowired private UserBalanceDao userBalanceDao; @Autowired private AbuserService abuserService; @Transactional public UserTransaction buyProduct(String userId, Product product, String account) { if (abuserService.isAbuser(userId, account) == true) { return null; } final UserBalance balance = userBalanceDao.getByUserId(userId); final int caratPrice = product.getPrice(); if (balance.getCurrentBalance() > caratPrice) { userBalanceDao.save(balance.addBalance(0 - caratPrice)); UserTransaction userTransaction = new UserTransaction().setUserId(userId).setCarats(caratPrice).setProductId(product.getProductId()) … userTransaction = userTransactionDao.save(userTransaction); return userTransaction; } return null; } }
  16. MVC - DAO All you have to do is extends

    CrudRepository<Model, ID type> interface. Spring framework will take care the others. // slidejoy/server/model/UserTransactionDao.java package com.slidejoy.server.model; import org.springframework.data.repository.CrudRepository; import java.util.List; public interface UserTransactionDao extends CrudRepository<UserTransaction, Long> { UserTransaction getByTransactionId(long transactionId); UserTransaction getByTransactionIdAndOrderId(long transactionId, int orderId); List<UserTransaction> getByUserId(String userId); } // slidejoy/server/model/UserBalanceDao.java public interface UserBalanceDao extends CrudRepository<UserBalance, String> { UserBalance getByUserId(String userId); }
  17. MVP pattern Presenter • View 1 <-> 1 Presenter ◦

    Lifecycle perspective ◦ Don’t pass presenter to singleton instance like manager.. • View doesn’t have any reference of presenter ◦ Because you can define different presenters for different models for same view • Event start from View? ◦ Easy to implement (As you can see...) • Events starts from Model? ◦ I recommend to follow observer pattern
  18. MVP - Presenter Keep in mind • Don’t import Presenter

    in View or Model • Before write a code in presenter, think one more whether it can be done in view or model layer. It will keep the presenter tidy and prevent view and model from becoming empty • What can we do with a monster presenter? public class TicTacToePresenter implements Board.EventListener, TicTacToeView.EventListener { private TicTacToeView view; private Board model; // Define event listeners here. You can still write a view structure related code in View classes. But all logics of view should be here. public TicTacToePresenter(TicTacToeView view) { this.view = view; this.model = new Board(); this.model.addEventListener(this); } @Override public void finalize() { // This is optional this.model.removeEventListener(this); } @Override public void onBoardChanged(Board model) { // Update view } // When the user selects a cell, our presenter only hears about what was (row, col) pressed, it's up to the view now to determine that from the Button that was pressed. @Override public void onButtonSelected(int row, int col) { Player playerThatMoved = model.mark(row, col); if(playerThatMoved != null) { view.setButtonText(row, col, playerThatMoved.toString()); if (model.getWinner() != null) { view.showWinner(playerThatMoved.toString()); } } } }
  19. MVP - Model Keep in mind • Model’s eventListener members

    should be a WeakReferenceMap ◦ Otherwise, you will have a memory leak public class Board { WeakReferenceMap<Object, EventListener> listeners = new WeakReferenceMap<> public void addListener(EventListener listener) { if (listeners.contains(listener) == false) { listeners.put(listener, listener); } } public void removeListener(EventListener listener) { if (listeners.contains(listener)) { listeners.remove(listener); } } … }
  20. Other rules • Try not to make a circular dependency

    ◦ There should be a way to prevent it. If it is not? let’s find what make it impossible and refactor it. • How to manage packages? ◦ Package by component - Activity / Service / EventReceiver ... ◦ Package by feature - Feed / Store / Ad / Content … ◦ Package by feature is preferred
  21. Other rules • Don’t make a function with more than

    50 lines. ◦ A function should be shown without a scroll. ◦ Don’t make a class with more than 1000 lines. ◦ The function name should represent all work done inside the function. • Don’t use static keyword w/o final ◦ Why? It is hard to follow where to initialize or update the field (ServerConfig.java) ◦ All static field w/o final can be written in Singleton design pattern • Think one more before creating xxManager and xxUtils ◦ Controller & Manager & Handler