南雅奇岩
基隆情人湖老鷹岩
嘉義北門車站
新港奉天宮
Total Pageviews
2017/02/28
2017/02/10
[Astah] How to Change Font Size in Astah?
Problem
How to change font size in Astah?
How-to
Go to Tool => Project => Default Font, you can change font, font style and font size in this dialog.
PS. Font configuration only allow in Professional version, Community version is disallowed.
How to change font size in Astah?
How-to
Go to Tool => Project => Default Font, you can change font, font style and font size in this dialog.
PS. Font configuration only allow in Professional version, Community version is disallowed.
2017/02/09
[Sentry] Fail to Create a New Project via REST API
Problem
I am using spring boot to implement web application, and trying to use sentry's rest API to create a new sentry project. I get this exception as I use rest API:
I try to use Advanced REST Client to do test, it succeed. Why?
How-To
Owing this http request is triggered at server side, so we cannot use browser's developer tool to debug.
You can go to telerik to download fiddler to help us to do web debug and to figure out what's the response it returns to us.
Fiddler is a free web debugging proxy which logs all HTTP(s) traffic between your computer and the Internet. Use it to debug traffic from virtually any application that supports a proxy like IE, Chrome, Safari, Firefox, Opera and more.
Before using fiddler, you need to set proxy to spring boot to send web traffic to Fiddler.
You can see all requests in fiddler, it looks like:
We also see the bad request, and find out the problem results from the rest URL had unnecessary blank (%20)
The response looks like (sentry tell it violate CSRF problem, but the real problem is the wrong REST URL):
Reference
[1] https://docs.sentry.io/hosted/api/teams/post-team-project-index/
I am using spring boot to implement web application, and trying to use sentry's rest API to create a new sentry project. I get this exception as I use rest API:
1 2 3 4 5 | org.springframework.web.client.HttpClientErrorException: 403 OK 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] |
I try to use Advanced REST Client to do test, it succeed. Why?
How-To
Owing this http request is triggered at server side, so we cannot use browser's developer tool to debug.
You can go to telerik to download fiddler to help us to do web debug and to figure out what's the response it returns to us.
Fiddler is a free web debugging proxy which logs all HTTP(s) traffic between your computer and the Internet. Use it to debug traffic from virtually any application that supports a proxy like IE, Chrome, Safari, Firefox, Opera and more.
Before using fiddler, you need to set proxy to spring boot to send web traffic to Fiddler.
You can see all requests in fiddler, it looks like:
We also see the bad request, and find out the problem results from the rest URL had unnecessary blank (%20)
The response looks like (sentry tell it violate CSRF problem, but the real problem is the wrong REST URL):
Reference
[1] https://docs.sentry.io/hosted/api/teams/post-team-project-index/
2017/02/08
The Bat and A Ball Problem
最近在看 Thinking, Fast and Slow 這本書,裡頭提到一個 The Bat and A Ball Problem,問題描述如下:
根據作者的實驗,超過 80% 的大學生會回答 0.1 元,但是這個答案卻是錯的,因為題目是說,球棒比球多 1 元,沒有說球棒是 1 元,人習慣靠直覺 (System 1) 來快速得到答案,因為比較省時、省力,但是 System 1 很容易犯錯
以下是運算式,X 代表球的價錢,最後推導出 X = 0.05
回答 0.1 的人,比較偏向直覺思考者,即運用 System 1 的人;回答 0.05 的人,比較偏向理性思考者,即運用 System 2 的人。
兩種 system 有其本質的差異:
If a baseball and a bat cost $1.10 together, and the bat costs $1.00 more than the ball, how much does the ball cost?
根據作者的實驗,超過 80% 的大學生會回答 0.1 元,但是這個答案卻是錯的,因為題目是說,球棒比球多 1 元,沒有說球棒是 1 元,人習慣靠直覺 (System 1) 來快速得到答案,因為比較省時、省力,但是 System 1 很容易犯錯
以下是運算式,X 代表球的價錢,最後推導出 X = 0.05
回答 0.1 的人,比較偏向直覺思考者,即運用 System 1 的人;回答 0.05 的人,比較偏向理性思考者,即運用 System 2 的人。
兩種 system 有其本質的差異:
系統一 (System 1):是 automatic system,屬於自動化、反射性、快速、不費力的思考方式。如運算 1 + 1 等於多少、看著報紙念出一則報導、理解一個簡單的句子等。但是,System 1 會自動運作且無法隨心所欲地關閉它,所以我們無可避免的會產生直覺錯誤、認知幻覺,System 1 的偏誤是無法避免的
系統二 (System 2):是 effortful system,屬於較費力、需專注力、邏輯思考、牽涉複雜運算、推理的思考方式。如填寫稅單、比較兩隻手機的優缺點、把車子停在一個格子較小的停車格。但是,System 2 用來取代 System 1 來做日常的決策是不切實際的,因為它運作緩慢且沒效率,我們能做的是:學習識別我們所處的情境的犯錯機率,當風險很高時要盡量避免犯下重大錯誤
Labels:
Reading
2017/02/07
[Angular2] Label Text Padding
Problem
This is my original modal dialog:
I hope the label can be looks like (it will be looks more pretty):
Here has the original code snippet:
How-To
We can add padding-left for each label, the padding-left property sets the left padding (space) of an element.
Reference
[1] http://www.w3schools.com/cssref/pr_padding-left.asp
This is my original modal dialog:
I hope the label can be looks like (it will be looks more pretty):
Here has the original code snippet:
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 | <modal #newUserModal [size]="'lg'"> <modal-header [show-close]="true"> <h4 class="modal-title">新增用戶</h4> </modal-header> <modal-body> <div class="row"> <button type="button" class="btn btn-primary btn-raised" style="float: right;" (click)="newUserModal.close();"> 取消 </button> <button type="button" class="btn btn-primary btn-raised" style="float: right;" (click)="createUser()"> 儲存 </button> </div> <div class="row"> <div class="col-sm-10 form-inline"> <label>帳戶名稱</label> <input type="text" class="form-control" id="login" [(ngModel)]="newUser.login" required> </div> </div> <div class="row"> <div class="col-sm-10 form-inline"> <label>名字</label> <input type="text" class="form-control" id="lastName" [(ngModel)]="newUser.lastName" required> </div> </div> <!-- ignore some code --> </modal-body> </modal> |
How-To
We can add padding-left for each label, the padding-left property sets the left padding (space) of an element.
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 | <modal #newUserModal [size]="'lg'"> <modal-header [show-close]="true"> <h4 class="modal-title">新增用戶</h4> </modal-header> <modal-body> <div class="row"> <button type="button" class="btn btn-primary btn-raised" style="float: right;" (click)="newUserModal.close();"> 取消 </button> <button type="button" class="btn btn-primary btn-raised" style="float: right;" (click)="createUser()"> 儲存 </button> </div> <div class="row"> <div class="col-sm-10 form-inline"> <label style="padding-left:64px">帳戶名稱</label> <input type="text" class="form-control" id="login" [(ngModel)]="newUser.login" required> </div> </div> <div class="row"> <div class="col-sm-10 form-inline"> <label style="padding-left:96px">名字</label> <input type="text" class="form-control" id="lastName" [(ngModel)]="newUser.lastName" required> </div> </div> <!-- ignore some code --> </modal-body> </modal> |
Reference
[1] http://www.w3schools.com/cssref/pr_padding-left.asp
Labels:
Angular2
2017/02/06
[Angular2] Dynamic Dropdown List
Problem
We have a dropdown list in the modal dialog:
The code snippet regarding dropdown list looks like:
If I would like to use dynamic way to generate this dropdown list instead of static way, how to do it?
How-to
Step 1. declare an array which contains the texts and values in the dropdown list, and given a name 'mailNotifications'
Step 2. Make good use of *ngFor to iterate its text and value for the dropdown list:
We have a dropdown list in the modal dialog:
The code snippet regarding dropdown list looks like:
<div class="row"> <div class="col-sm-10 form-inline"> <label>電子郵件提醒事項</label> <select class="form-control" id="mailNotification" [(ngModel)]="newUser.mailNotification"> <option value="all">提醒與我的專案有關的全部事件</option> <option value="only_my_events">只提醒我觀察中或參與中的事物</option> <option value="only_assigned">只提醒我被分派的事物</option> <option value="only_owner">只提醒我作為擁有者的事物</option> <option value="none">取消提醒</option> </select> </div> </div>
If I would like to use dynamic way to generate this dropdown list instead of static way, how to do it?
How-to
Step 1. declare an array which contains the texts and values in the dropdown list, and given a name 'mailNotifications'
import { Component } from '@angular/core'; import { CORE_DIRECTIVES } from '@angular/common'; import * as bootbox from 'bootbox'; @Component({ selector: 'user-list', templateUrl: 'user.list.html' }) export class UserComponent { searchUser: User = new User(); newUser: User = new User(); mailNotifications: Array<any> = [ { text: '提醒與我的專案有關的全部事件', value: 'all' }, { text: '只提醒我觀察中或參與中的事物', value: 'only_my_events' }, { text: '只提醒我被分派的事物', value: 'only_assigned' }, { text: '只提醒我作為擁有者的事物', value: 'only_owner' }, { text: '取消提醒', value: 'none' } ]; ngOnInit() { this.searchUser.status = 1; } }
Step 2. Make good use of *ngFor to iterate its text and value for the dropdown list:
<div class="row"> <div class="col-sm-10 form-inline"> <label>電子郵件提醒事項</label> <select class="form-control" id="mailNotification" [(ngModel)]="newUser.mailNotification"> <option *ngFor="let n of mailNotifications" [ngValue]="n.value">{{n.text}}</option> </select> </div> </div>
Labels:
Angular2
2017/02/05
[Testing Tool] Postman
I am using Redmine Rest API to implement my web application. For example, here has developer guide to teach us how to use rest API to find, create, update and delete project: https://www.redmine.org/projects/redmine/wiki/Rest_Projects
In order to make sure the JSON format for each action (i.e. CRUD) in request or response, we can make good use of Postman to assist us.
Installation
How-to
Firstly, we need to configure user name and password for logging into Redmine:
Secondly, we need to set content-type to application/json in Headers:
If I would like to test:
Reference
[1] https://www.getpostman.com/
[2] https://dzone.com/articles/12-great-web-service-testing-tools?utm_content=bufferba092&utm_medium=social&utm_source=twitter.com&utm_campaign=buffer
In order to make sure the JSON format for each action (i.e. CRUD) in request or response, we can make good use of Postman to assist us.
Installation
How-to
Firstly, we need to configure user name and password for logging into Redmine:
Secondly, we need to set content-type to application/json in Headers:
If I would like to test:
- List projects: GET /projects.json
- Select GET HTTP method, fill in URL and click Send button, then we can get the execution result
- Show a project: GET /projects/[id].json
- Select GET HTTP method, fill in URL and click Send button, then we can get the execution result
- Create a project: POST /projects.json
- Select POST HTTP method, fill in URL, prepare JSON string for creating a new project and click Send button, then we can get the execution result
- Update a project: PUT /projects/[id].json
- Select PUT HTTP method, fill in URL, prepare JSON string for updating project and click Send button, then we can get the execution result
- Delete a project: DELETE /projects/[id].json
- Select DELETE HTTP method, fill in URL and click Send button, then we can get the execution result
Reference
[1] https://www.getpostman.com/
[2] https://dzone.com/articles/12-great-web-service-testing-tools?utm_content=bufferba092&utm_medium=social&utm_source=twitter.com&utm_campaign=buffer
2017/02/04
[Design Pattern] Strategy Pattern
Assume I have a data exporter which can export data to PDF, XLS or HTML format. It can export data to different format depends on your demand.
Firstly, creating a strategy interface:
Secondly, creating concrete classes implements strategy interface.
The DataExport class will export data depends on client's demands.
We create objects which represent various export strategies, but we still expose if-else logic in client. Therefore, we create an ExportStrategy class which will change the executing algorithm of the context object (i.e. PdfExporter, HtmlExporter, and ExcelExporter).
And the DataExport will be modified as bellows:
To sum up, we create a ExportStrategy interface which define an exportData action and concrete strategy classes (i.e. PdfExporter, ExcelExporter, HtmlExporter) implementing the ExportStrategy interface. ExporterContext is a class which uses a Strategy.
DataExport, our demo class, use Context and strategy objects to demonstrate change in Context behaviour based on strategy (i.e. PdfExporter, ExcelExporter, HtmlExporter) it uses.
Firstly, creating a strategy interface:
package albert.practice.designpattern.strategy; public interface ExportStrategy { void exportData(String data); }
Secondly, creating concrete classes implements strategy interface.
package albert.practice.designpattern.strategy; import lombok.extern.slf4j.Slf4j; @Slf4j public class ExcelExporter implements ExportStrategy { @Override public void exportData(String data) { log.debug("exporting " + data + " into excel file."); } }
package albert.practice.designpattern.strategy;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class HtmlExporter implements ExportStrategy {
@Override
public void exportData(String data) {
log.debug("exporting " + data + " into html file.");
}
}
package albert.practice.designpattern.strategy;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class PdfExporter implements ExportStrategy {
@Override
public void exportData(String data) {
log.debug("exporting " + data + " into pdf file.");
}
}
The DataExport class will export data depends on client's demands.
package albert.practice.designpattern.strategy; public class DataExport { public static void main(String[] args) { String data = "[test data]"; DataExport exporter = new DataExport(); exporter.exportData(FileTypeEnum.XLS, data); exporter.exportData(FileTypeEnum.PDF, data); exporter.exportData(FileTypeEnum.HTML, data); } public void exportData(FileTypeEnum fileType, String data) { ExportStrategy exporter = null; if (FileTypeEnum.XLS == fileType) { exporter = new ExcelExporter(); } else if (FileTypeEnum.PDF == fileType) { exporter = new PdfExporter(); } else if (FileTypeEnum.HTML == fileType) { exporter = new HtmlExporter(); } exporter.exportData(data); } }
We create objects which represent various export strategies, but we still expose if-else logic in client. Therefore, we create an ExportStrategy class which will change the executing algorithm of the context object (i.e. PdfExporter, HtmlExporter, and ExcelExporter).
package albert.practice.designpattern.strategy; import lombok.Data; @Data public class ExporterContext { private String data; public void doExport(ExportStrategy strategy) { strategy.exportData(this.data); } }
And the DataExport will be modified as bellows:
package albert.practice.designpattern.strategy; public class DataExport { public static void main(String[] args) { String data = "[test data]"; ExporterContext context = new ExporterContext(); context.setData(data); context.doExport(new HtmlExporter()); context.doExport(new ExcelExporter()); context.doExport(new PdfExporter()); } }
To sum up, we create a ExportStrategy interface which define an exportData action and concrete strategy classes (i.e. PdfExporter, ExcelExporter, HtmlExporter) implementing the ExportStrategy interface. ExporterContext is a class which uses a Strategy.
DataExport, our demo class, use Context and strategy objects to demonstrate change in Context behaviour based on strategy (i.e. PdfExporter, ExcelExporter, HtmlExporter) it uses.
Labels:
Design Pattern
2017/02/03
[Git] Commit undo process
Problem
If we would like to clean up my commits, i.e. remove some unnecessary commits, how to do it?
How-to
If we would like to revert to this commit which number is ead950a
The commands are as bellows:
The result is as bellows:
If we would like to clean up my commits, i.e. remove some unnecessary commits, how to do it?
How-to
If we would like to revert to this commit which number is ead950a
The commands are as bellows:
The result is as bellows:
Labels:
Git
2017/02/02
2017/02/01
[閱讀筆記] The World Is Flat
- 雖然很多美國的會計師的工作都外包到印度了,但是想留在美國繼續做這一行的會計師必須要懂得設計具有創意的複雜策略,才有辦法繼續生存,例如如何避稅、節稅、經營客戶關係。這類會計師會對客戶說:「我把旁枝末節的報稅工作外包到國外去了,我們現在來討論,如何料理你的不動產,你打算為客戶做哪些安排,你準備把一些前留在信託基金裡嗎?」
- 任何工作,只要能夠把內容數位化,把流程切割,靈活調動每一個部分,就可能被包到海外。我無法幫你端上一客牛排,可是我可以為你在全世界的任何餐廳訂位
- 何處可以把工作做的最符合效益又有效率,工作就會移往何處。如此一來,釋放了人力與資金去做更高層次的工作,而且我們能以更低的成本做生產,使消費者與企業互蒙其利
- 市場經濟的規則是,哪裡有最豐沛的人力資源、最低廉的勞工,企業自然就會往那兒去
- 景氣過熱和泡沫可能誘發經濟危機,會使很多人、很多企業虧損甚至破產,不過也能驅使科技的創新越來越快。所造成的過剩生產力不論是發生在鐵路舖設或汽車生產上,最後都能帶來意想不到的正面效果
- 標準化並不會阻礙創新,只會讓人更專注,專注於真正價值所在。價值通常可以附加在標準之上或其周邊
- 軟體是生菜不是黃金,是容易朽壞的商品,如果一段時間不改善,就會腐爛
- 很多產品是互補的,有了A 如果也有 B,A 的價值就會更高。有紙很好,有鉛筆也很好,有了更多紙就會有更多鉛筆;再來紙的品質變好了,鉛筆也變好,結果生產力就會提昇。這就是互補性產品同步改進的現象
- 只是引進新科技是絕對不夠的,新科技一定要結合新作法,產能才會升級
- 資訊科技的出現並沒有馬上提高產能,因為有了新電腦還不夠,還需要新程序核心技能。新作法會增加資訊科技的價值,更新更好的資訊科技則會增加新作法的可能
- 網路泡沫化,並沒有終結全球化,反而逼得許多企業盡可能把工作外包出去,以節省資金,等於加速全球化
- 當創造價值模式從垂直變水平,企業從計畫與控制變成連結與合作
- 價值通常是同一家公司由上而下創造的,很容易看出誰在上誰在下,誰剝削誰。但是當世界開始抹平,價值是水平創造的,合作的方式有好多種,小人物的力量比以前大很多,誰在上誰在下,誰封鎖誰,一切變得很複雜
- 假如每一國只生產自己擁有成本優勢的產品,再與他國交易他國所擁有優勢的商品,每個貿易國都會受益,國民所得全會提升
- 出售勞力的人,一次只能賣給一家工廠或因為消費者,不像軟體或新藥的發明者,同時可以賣給全球市場的所有人
- 當印度或中國往價值鏈的上端移動,開始生產美國以往擅長的知識產品,美國在某些領域的比較優勢就會減少。某些領域的工資會下跌,有些工作甚至會永遠移往海外。所以某些知識工作者必須水平移動。但是變大的全球大餅一定只會創造出目前無法預測的新專業,留待工作者去填滿
- 在抹平的世界,你必須太特殊、太專業、太懂得深耕、太會調適,你的工作才不會外包出去
- 工程師出身的領導者,聽你說話馬上就能抓住重點;律師出身的領導者,都聽不到重點
- 科技與貿易不僅把餅越做越大,還把餅的配額由低階工作轉到高階。把高中教育變成義務,社會就多出許多技術較高的工作者,可以在變大、變複雜的經濟大餅中,吃到更大的一塊
- 專才通常技術有深度,卻視野窄;通才視野廣,技術卻沒深度,需要反應的時候行動快,卻常得不到夥伴或客戶的信任。相形之下,博才則能在日益寬廣的狀況與經驗中應用深度技術,一直能學新技能,建立新關係,擔當新角色。公司已不能把人當專門工具,員工也不能再繼續當專門工具,必須改當瑞士刀。瑞士刀便是博才
- 在抹平世界的過程,一定會創造贏家與輸家,要思索的是讓贏家補償輸家的機制,尤其是原本高薪專業,再就業薪水卻少很多的輸家。若你不處理,就是在自惹政治反彈。抹平的過程一定會有很多受害者,只要經濟長期衰退,反彈就可能會變成動亂
- 假如社會無法處理世界抹平所產生的緊張,就會有反彈,會有政治勢力想把推土機所剷除的障礙與藩籬重新豎起,他們會以保護弱者為名,到頭來大家的生活水平都要因此降低
- 我們應該善待弱者及落後的人,要挺抹平的唯一方式,就是做個有愛心的抹平主義者。即想過著共和黨人那種日子,投票就要像民主黨人(註:通常共和黨選民比較富有,民主黨選民比較重視社會福利)
- 保障就業是沒辦法讓國家脫貧的。問題只是就業就好解決,國營企業可以吸收一切需要就業的人口。問題不只是就業,而是可以提升生活水平,增加生產力的那種就業
- 政府不只要由上而下負責把財政及貨幣政策管好,還要創造一個易於創業、籌資、經商的民間環境,並容許部分外國競爭,因為有競爭,創新總是較多較快
Labels:
Reading
2017/01/31
2017/01/11
[Oracle VirtualBox] VT-x is not available
Problem
當我們參加教育訓練,從講師的硬碟 copy 上課用的 VirtualBox 到自己的電腦後,在啟動的時候發生錯誤,顯示 VT-x is not available 的錯誤訊息
How-to
解決步驟如下:
1. 重新啟動電腦 (restart your computer)
2. 按下 Esc,到 BIOS 進行設定 (press Esc during restarting computer process, then enter your BIOS configuation)
3. 將 Virtualization Technology (VTx) 的選項選取,重新啟動電腦即可 (Check Virtualization Technology (VTx) and Save, then restart your computer)
當我們參加教育訓練,從講師的硬碟 copy 上課用的 VirtualBox 到自己的電腦後,在啟動的時候發生錯誤,顯示 VT-x is not available 的錯誤訊息
How-to
解決步驟如下:
1. 重新啟動電腦 (restart your computer)
2. 按下 Esc,到 BIOS 進行設定 (press Esc during restarting computer process, then enter your BIOS configuation)
3. 將 Virtualization Technology (VTx) 的選項選取,重新啟動電腦即可 (Check Virtualization Technology (VTx) and Save, then restart your computer)
Labels:
VirtualBox
2017/01/10
[Oracle VirtualBox] 在 VirtualBox 中無法連上網路
Problem
當我啟動 VirtualBox 後,發現在 VirtualBox 中無法連上網路,但是在本機是可以連上網路的,此 VirtualBox 的網路設定如下:
How-To
網路要設定成橋接介面卡,並指定本機使用到的網路介面卡即可
當我啟動 VirtualBox 後,發現在 VirtualBox 中無法連上網路,但是在本機是可以連上網路的,此 VirtualBox 的網路設定如下:
How-To
網路要設定成橋接介面卡,並指定本機使用到的網路介面卡即可
Labels:
VirtualBox
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.
Each juice will implement Juice interface, and implement its own logic in each class.
Here is apple juice:
Here is orange juice:
Here is kiwi juice:
I also create a fruit enumeration to tell which juice I will make:
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.
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.
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:
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.
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.
Labels:
Design Pattern
Subscribe to:
Posts (Atom)