Total Pageviews

2017/01/09

[Design Pattern] Factory Pattern

Assume I have a juice store, which provide apple juice, orange juice and kiwi juice. As I accept customer's order, I will make juice and deliver juice to customer.

Therefore, I will do make juice and deliver juice no matter which juices are ordered.
package albert.practice.designpattern.factory;

public interface Juice {
    void makeJuice();
    void deliverJuice();
}


Each juice will implement Juice interface, and implement its own logic in each class.
Here is apple juice:

package albert.practice.designpattern.factory;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class AppleJuice implements Juice {

    @Override
    public void makeJuice() {
      log.debug("making APPLE juice.");
    }

    @Override
    public void deliverJuice() {
        log.debug("deliver APPLE juice to customer.");
    }

}



Here is orange juice:

package albert.practice.designpattern.factory;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class OrangeJuice implements Juice {

    @Override
    public void makeJuice() {
        log.debug("making ORANGE juice.");
    }

    @Override
    public void deliverJuice() {
        log.debug("deliver ORANGE juice to customer.");        
    }

}



Here is kiwi juice:
package albert.practice.designpattern.factory;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class KiwiJuice implements Juice {

    @Override
    public void makeJuice() {
        log.debug("making KIWI juice.");
    }

    @Override
    public void deliverJuice() {
        log.debug("deliver KIWI juice to customer.");        
    }

}


I also create a fruit enumeration to tell which juice I will make:
package albert.practice.designpattern.factory;

public enum FruitEnum {
    APPLE, ORANGE, KIWI;
}


And I create a JuiceStore to process customers' order. The processOrder method is used to create apple, orange or kiwi object depends on customer's order.
package albert.practice.designpattern.factory;

public class JuiceStore {

    public static void main(String[] args) {
        JuiceStore test = new JuiceStore();
        test.processOrder(FruitEnum.KIWI);
        test.processOrder(FruitEnum.APPLE);
        test.processOrder(FruitEnum.ORANGE);
    }

    public void processOrder(FruitEnum fruit) {
        Juice juice = null;
        if (FruitEnum.APPLE == fruit) {
            juice = new AppleJuice();
        } else if (FruitEnum.ORANGE == fruit) {
            juice = new OrangeJuice();
        } else if (FruitEnum.KIWI == fruit) {
            juice = new KiwiJuice();
        }
        juice.makeJuice();
        juice.deliverJuice();
    }

}


It seems good so far. But if we would like to create object without exposing the if-else creating logic to client, we need to apply factory pattern to refactor the existing architecture.


Therefore, we need to create a JuiceFactory to generate object of concrete class based on given fruit.
package albert.practice.designpattern.factory;

public class JuiceFactory {

    public Juice getJuice(FruitEnum fruitEnum) {
        Juice juice = null;
        if (FruitEnum.APPLE == fruitEnum) {
            juice = new AppleJuice();
        } else if (FruitEnum.ORANGE == fruitEnum) {
            juice = new OrangeJuice();
        } else if (FruitEnum.KIWI == fruitEnum) {
            juice = new KiwiJuice();
        }
        return juice;
    }

}



We can see the updated JuiceStore class, the creating logic had been hide in JuiceFactory, we only need to tell JuiceFactory which fruit I would like to make:

package albert.practice.designpattern.factory;

public class JuiceStore {

    public static void main(String[] args) {
        JuiceFactory factory = new JuiceFactory();
        
        Juice apple = factory.getJuice(FruitEnum.APPLE);
        apple.makeJuice();
        apple.deliverJuice();
        
        Juice orange = factory.getJuice(FruitEnum.ORANGE);
        orange.makeJuice();
        orange.deliverJuice();
        
        Juice kiwi = factory.getJuice(FruitEnum.KIWI);
        kiwi.makeJuice();
        kiwi.deliverJuice();
    }
}


We can see JuiceStore asks JuiceFactory to create object based on its demands, and JuiceStore will use its object to make juice and deliver juice.




2017/01/08

[Redmine] Fail to Create Project via Rest API again!

Problem
I am using Redmine rest api to create project, and had checked the project identifier only allowed lower letter (a-z), numbers, dashes and underscores. But it still go wrong when I only provide numbers in project identifier:
1
2
3
4
5
org.springframework.web.client.HttpClientErrorException: 422 Unprocessable Entity
 at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
 at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:667) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
 at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:620) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
 at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:580) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]


How-to
According to this issue, the project identifier cannot accept number only although the document does not say it.

Therefore, we need to do one more check as we create project (you need to import org.apache.commons.lang.StringUtils class):
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    private void checkProjectIdentifier(String identifier) {
        Pattern pattern = Pattern.compile("^[a-z0-9_=]*$");
        if (!pattern.matcher(identifier).matches()) {
            throw new RuntimeException("專案代碼僅允許使用小寫英文字母 (a-z), 阿拉伯數字, 虛線與底線");
        }
        
        if(StringUtils.isNumeric(identifier)) {
            throw new RuntimeException("專案代碼不可僅有數字");
        }
    }


Reference
[1] https://www.redmine.org/issues/5877

2017/01/07

[Redmine] Fail to Create Project via Rest API

Problem
I am using Redmine Rest API to create project, but I get this error message:
1
2
3
4
5
6
org.springframework.web.client.HttpClientErrorException: 422 Unprocessable Entity
 at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
 at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:667) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
 at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:620) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
 at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:580) ~[spring-web-4.3.2.RELEASE.jar:4.3.2.RELEASE]
 at com.cht.commons.web.client.RestRequest.post(RestRequest.java:175) ~[cht-commons-web-client-0.3.0-SNAPSHOT.jar:0.3.0-SNAPSHOT]




How-To
This error results from the project identifier's limitation. 
I provide an illegal project identifier, so I fail to create project.



Owing to the Redmine Rest API cannot provide meaningful information for user, so I need to utilize regular expression to provide meaningful information when the project identifier is not legal.
1
2
3
4
5
6
    private void checkProjectIdentifier(String identifier) {
        Pattern pattern = Pattern.compile("^[a-z0-9_=]*$");
        if (!pattern.matcher(identifier).matches()) {
            throw new RuntimeException("僅允許使用小寫英文字母 (a-z), 阿拉伯數字, 虛線與底線");
        }
    }

    
    
Reference
[1] https://www.redmine.org/projects/redmine/wiki/Rest_Projects    


2017/01/06

[Angular2] Services

Requirement
It's very common that multiple components may need access to the same code and we don't want to copy and paste the same code over and over. 
Therefore, we need create a single reusable data service and inject it in the components that need it.

How-To
(1) Create a service class looks like:
1
2
3
4
5
6
7
8
9
import { Injectable } from '@angular/core';
import { Http, Response, Headers } from '@angular/http';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class ProjectService {

  constructor(private http: Http) { }
}

(2) Import, register and initialize the service class in component class
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { Component, Pipe, PipeTransform } from '@angular/core';
import { CORE_DIRECTIVES } from '@angular/common';
import { TAB_DIRECTIVES } from 'ng2-bootstrap/ng2-bootstrap';
import { Router } from '@angular/router';
// import ProjectService
import { ProjectService } from './project.service';
import { MODAL_DIRECTIVES, ModalComponent } from 'ng2-bs3-modal/ng2-bs3-modal';
import { PAGINATION_DIRECTIVES } from 'ng2-bootstrap/ng2-bootstrap';
import { StatusPipe } from './project.pipe';

@Component({
  selector: 'project-list',
  templateUrl: 'project.list.html',
  directives: [MODAL_DIRECTIVES, PAGINATION_DIRECTIVES],
  providers: [ProjectService], // register ProjectService to providers
  pipes: [StatusPipe]
})

export class ProjectComponent {
  
  // initialize ProjectService in constructor
  constructor(private projectService: ProjectService) {

  }
}

(3) If I add a search method in ProjectService:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import { Injectable } from '@angular/core';
import { Http, Response, Headers } from '@angular/http';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class ProjectService {

  constructor(private http: Http) { }
  
  search(searchProject: Project, page: number) {
    let name: string = searchProject.name;
    let status: number = searchProject.status;
    return this.http.get('/api/projects/search?name=' + name + '&status=' + status + '&page=' + page)
                    .map(res => <Page<Project>>res.json());
  }
}

(4) The component class code snippet looks like:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import { Component, Pipe, PipeTransform } from '@angular/core';
import { CORE_DIRECTIVES } from '@angular/common';
import { TAB_DIRECTIVES } from 'ng2-bootstrap/ng2-bootstrap';
import { Router } from '@angular/router';
// import ProjectService
import { ProjectService } from './project.service';
import { MODAL_DIRECTIVES, ModalComponent } from 'ng2-bs3-modal/ng2-bs3-modal';
import { PAGINATION_DIRECTIVES } from 'ng2-bootstrap/ng2-bootstrap';
import { StatusPipe } from './project.pipe';
import { Project, Page } from '../model/model';

@Component({
  selector: 'project-list',
  templateUrl: 'project.list.html',
  directives: [MODAL_DIRECTIVES, PAGINATION_DIRECTIVES],
  providers: [ProjectService], // register ProjectService to providers
  pipes: [StatusPipe]
})

export class ProjectComponent {
  
  projects: Page<Project>;
  
  // initialize ProjectService in constructor
  constructor(private projectService: ProjectService) {  }
  
  search() {
    this.projectService.search(this.searchProject, this.currentPage).subscribe(
          res => {
            this.projects = res;
          }
    );
  }

}



2017/01/05

[Angular2] Custom Pipe

Problem
I retrieve a collection of data with json data type, the json structure looks like:


These data will be displayed in data grid:


The second column displays status code, but I would like to display its description instead of code (i.e. 1:使用中、5:已關閉、9:已封存) :


How to do it?

How-To
Utilize custom pipe can solve this problem:

Step1. Create a custom pipe
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({ name: 'statusPipe' })
export class StatusPipe implements PipeTransform {
  transform(status: number) {
    let statusName = '';
    if (status == 1) {
      statusName = '使用中';
    } else if (status == 5) {
      statusName = '已關閉';
    } else if (status == 9) {
      statusName = '已封存';
    }
    return statusName;
  }
}


Step2. Import custom pipe to your component. Here is the code snippet:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
//...
import { StatusPipe } from './project.pipe';

@Component({
  selector: 'project-list',
  templateUrl: 'project.list.html',
  directives: [MODAL_DIRECTIVES, PAGINATION_DIRECTIVES],
  providers: [ProjectService],
  pipes: [StatusPipe]
})

export class ProjectComponent {

  searchProject: Project = new Project();
  projects: Page<Project>;

  constructor(private projectService: ProjectService, private appNotificationSerivce: AppNotificationService) {
  }
  //...
}  


Step3. Apply pipe to your html page. Here is the code snippet:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
  <div class="well" *ngIf="projects && projects.content.length > 0">
    <div class="table-responsive">
      <table class="table">
        <thead>
          <tr>
            <th width="70%">專案名稱</th>
            <th width="20% ">狀態</th>
            <th width="10% "></th>
          </tr>
        </thead>

        <tbody>
          <tr *ngFor="let project of projects.content; let rowIdx=i ndex ">
            <td> {{ project.name }}</td>
            <td> {{ project.status | statusPipe }}</td>
            <td> </td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>



2017/01/04

[VS Code] How to configure proxy setting?

Problem
If I would like to change the proxy setting in Visual Studio Code, how to do?

How-To
Step1. Open setting.json


Then you can see the default setting values in setting.json


Step2. Set your proxy values to overwrite the default values



Reference
[1] https://code.visualstudio.com/docs/customization/userandworkspace

2017/01/03

[Angular2] Fail to install npm package

Problem
When I try to install npm package...
Command:
npm install -g angular-cli@webpack

I get this error message:
npm ERR! network If you are behind a proxy, please make sure that the
npm ERR! network 'proxy' config is set properly.  See: 'npm help config'
npm ERR! Please include the following file with any support request:

npm ERR!     C:\Users\chtti\npm-debug.log


How-to
The problem results from you don't configure proxy properly. Therefore, you need to set your corporate web proxy before you install npm package.
The command is as bellows:
npm config set proxy [your proxy]

Reference
[1] https://jjasonclark.com/how-to-setup-node-behind-web-proxy

2017/01/02

[Eclipse] "Annotation processor 'lombok.core.AnnotationProcessor' not found"

Problem
I fail to startup my Spring boot application, and get this error : "Annotation processor 'lombok.core.AnnotationProcessor' not found".

My tools is :
(1) Eclipse with Mars version
(2) Spring Boot 1.4
(3) JDK 1.8
(4) Lombok 1.16.10


How-To
The root cause is still unknown. But after I reinstall Project Lombok jar file, the problem has been fixed.

Steps:
(1) Close Eclipse
(2) Download jar file from https://projectlombok.org/
(3) Install Lombok
(4) Launch Eclipse


2017/01/01

[閱讀筆記] Wikinomics

  1. Wikinomics的四個原則:openness (開放), peering (對等), sharing (分享) 與 acting globally (全球運作)
  2. 現代的企業,與其像音樂、影視業者不斷地建造高聳的藩籬來保護公司的智慧財產與打擊盜版。聰明的企業應該把公司的智慧財產 (intellectual property) 視為共同基金 (mutual fund),將公司的智慧資產 (intellectual property assess, IP assets) 當作一個均衡的投資組合 (balanced portfolios) 來做管理,有些 IP assets予以保護,有些 IP assets 則分享給大眾,掌握客戶導向創新的機會,說不定可以協助公司創造出新的 business model 或進入不同的 industries。
  3. 要保持全球競爭力的公司,必須持續關注全球事業的發展、廣納全球的人才庫。透過全球的同盟、人力資本市集 (human capital marketplaces) 與同儕生產社群 (peer production communities) 來幫企業引入新的想法、市場、點子與技術。
  4. 在新的時代,人們逐漸不相信媒體與廣告,人們反而比較信任同儕的意見與社群網路
  5. Coase’s Law 的重點在於 transaction costs,交易成本過高,會影響交易的進行。隨著網路的興起,交易成本大幅降低,促進全球化交易的進行
  6. Peer production 能夠運行,至少要符合三個條件:
    1. 成員間的溝通成本低
    2. 任務可以切成一小塊一小塊,讓每個人在每一小塊做出貢獻,最後將每一小塊組成最終的產品或服務
    3. 整合每一小塊的成本也要低,包含社群間的領導機制、品質控管機制等
  7. Collaborative outsourcing 比較適合的是:公司產品中非核心的部分,或公司 business model 非核心的部分。以 IBM 來說,其 Web Servers 與 OS 落後其他競爭者一大截,擁抱 open source 並不會讓自己失去什麼,反而因為 peer production 而產生正向的效果
  8. Open source 的三個原則:沒有人擁有它每個人都可以使用它任何人都可以改進它。這也是open source 無止盡的創新的來源,但也帶給 IT 主管無盡的挫折,因為有太多的選擇,你必須挑選出品質優良、適用的 open sources,並將其組裝起來並運行正確
  9. 企業與大學建立夥伴關係,是加速創新與成長的好方法,但是要注意:
    1. 利用企業與大學的夥伴關係來整理出未來的產品線:企業必須要有新的刺激才會導入突破性的元素,開創新的產品
    2. 確保雙方是雙贏的關係:如此關係才會長久
    3. 研究社群間的合作要夠深且夠廣:不同社群可產生不同想法且產生綜效,不只是研究社群提供 inputs 給企業,企業也可提供解決方案給社群,加速研究腳步
    4. 技術的知識保持開放,最終的應用保持專屬(proprietary)
    5. 讓具代表性的客戶及早且時常參與,提供回饋:每當做出一個雛形就讓 early users 使用並提供回饋,不斷修正
  10. 對於企業來說,如何開放一個與外界合作的平台,是加速創新成功與擴大創新範圍的鎖鑰。每個企業的經營者都要問自己:
    1. 要如何建立一個與外界合作的平台?
    2. 選擇哪些事業要開放,如何、何時與在哪裡開放?
    3. 如何吸引外界的人才到此開放平台,一起合作與創新?
  11. 以Boeing為例,從一個飛機製造商變成整合成千上萬的合作夥伴的腳色,你必須要:
    1. 了解市場的需求且確保需求是可行的
    2. 必須將你所了解的轉換成需求
    3. 必須將所有的合作夥伴整合起來並朝向共同的目標前進 (這是最困難的部分)
    4. 建立一個合作的平台,可以讓所有合作夥伴在平台上即時溝通、分享資訊、進行元件/零件模擬測試相容性,避免到製造階段才發現不合用
  12. 將設計與開發產品的工作,從原本都是自己做,變成與供應商一起協同合作,這對雙方來說都是有利的。對於供應商來說,透過協同研發,可以增加公司的智慧財產,並且可以在終端產品上得到可觀的利潤。對於自身來說,變成整合者的角色,可以加速上市速度、公司敏捷度與具焦於高附價加值的活動
  13. 從 Boeing, BMW的身上我們可以學到:
    1. 聚焦於關鍵的價值驅動力:如汽車業應該從過去的製造汽車,變成設計 interface,讓合作夥伴們能順利的合作與與軟硬體測試
    2. 透過管理合作夥伴與協同合作來增加彼此的價值
    3. 透過協同合作的平台,加速設計流程
    4. 採用模組化的架構
    5. 建立一個透明的、人人平等的生態系統
    6. 大家一起分享決定權、利潤、成本與風險
    7. 敏銳地關注未來趨勢
  14. 傳統的工作場所像是個聽著軍樂、一個口令一個動作在行軍的軍隊;在未來,工作場所比較像是個爵士樂團,每個音樂家以有創意的方式,即興演奏旋律與節奏在座演奏。員工會自己建立與外界溝通的管道,形成跨功能的團隊並與外界做即時的溝通
  15. 未來的公司,不會是還停留在是否要參與、或是否要與 peer-production comminities 合作,他是要決定何時要加入這些社群,以及如何與這些社群合作,發揮綜效
  16. 對於公司經營者來說,有關 Openness,你要注意
    1. 你的競爭者在創新什麼東西?你要聚焦於如何做出不一樣的東西,與競爭對手差異化?
    2. 你的員工是否知道正確的 knowledge-creating networks,並從中獲取資訊?
    3. 有哪些產品、流程或資產你可以開放出來給大家知道,藉此降低 R&D 成本、提升成長? 以及如何增加所經營的 ecosystem 的參與人數?
    4. 你要決定你要加入哪個 ideagoras (http://www.ideagoras.biz/) 去獲得關鍵的創新材料,或是將你的創新授權給別人使用
  17. 有哪些 Intellectual Property & Proprietary 要開放,以下有幾個簡單的準則
    1. 若你提供的Proprietary產品或服務的市占率一直下降,不妨將其 open 出來,或許可以注入新的創意與人才,為日漸下滑的產品或服務注入新生命
    2. 若有些問題或瓶頸,內部一直無法突破,不妨將問題 open 出來,並提供相關的資訊與協助,擴展解決問題的人才庫
    3. 你是否有一個開放的平台,讓你的 ecosystems 裡面的夥伴們,能夠有效率的合作與創新
    4. 在與外部合作的專案,資訊要公開、透明,如此一來,參與者才會專注於手邊的工作,而非專注在爭奪專利所有權
  18. Enterprise 2.0 的企業是,打破企業高牆、善用外部知識、資源、能力與人才。讓自己成為創新中心,像個磁鐵把人才聚集起來。內部員工聚焦於價值整合與管理,將全世界當作是它們的研發部門