Total Pageviews

2020/02/01

[閱讀筆記] Learn to Think in Systems (2/7)

  1. 長期計畫通常需要較長的時間、較多的力氣與較多的犧牲。因此,對於人們來說通常較不具吸引力,因為人們總是想要立刻看到成果。
  2. 我們是唯一擁有後設認知能力 (ability of metacognition) 的生物,我們可以思考我們的想法;我們可以思考與接受我們生物上的限制與現實,並想辦法予以克服;我們可以選擇延後快樂,我們可以選擇長期的解決方案,讓我們在未來的時候更快樂;當我們面對複雜問題時,可以選擇採用 systemic thinking 而非 liner thinking。
  3. Systems problems 通常非常複雜且具有以下特性
    1. 並非每日常見的問題;
    2. 問題涵蓋範圍很大;
    3. 無法有足夠的時間、知識,來思考解決之道;
    4. 常常需要更多資訊、團隊合作與較高階的思考技巧或工具
  4. 詢問以下問題,是 systems thinking 的基本步驟 
系統思考的基本步驟
Examples
過去發生什麼問題? (what)
Acme 產品銷量不斷下滑,儘管銷售團隊努力帶入新的顧客。此 problem 引入下一個 question。
問題發生的原因是什麼? (why)
顧客面臨一堆服務相關議題,包含產品交付延遲、帳務錯亂議題,導致顧客流失,system thinker 需探究以上問題的成因,而非採用 linear thinking,交付延遲就推動自動交貨系統、帳務錯亂就提出帳務系統升級。
如何改進系統的績效? (how)
以 Acme 的問題為例,由於銷售團隊過度重視挖掘新客戶,提供特殊的產品,導致產品交付延遲、帳務錯亂,進一步造成既有客戶流失、新客戶也爭取不到的狀況。即使尋找新客戶是重要的,Acme 應該要設計策略來與現有顧客維持良好關係。


  1. Linear thinking:只看問題表象,交付延遲就推動自動交貨系統、帳務錯亂就提出帳務系統升級
  2. Systems thinking:R2 (Reinforcing feedback) 才是客戶流失的主因,銷售部門不斷提出新的包裝來爭取新顧客,導致服務部門負擔過重、帳務系統與交付系統不適用新的商品,造成帳務出錯、交付延遲的問題,進一步造成顧客抱怨、客戶流失、銷售下滑。銷售部門想要守住客戶數與銷售量,加強新產品銷售力道,反而導致服務部門負擔更重、帳戶系統與交付系統一段混亂,導致惡性循環。
  3. 你該採用 system thinking 的原因
  • 能針對長久以來且看起來抗拒改變的問題,提出珍貴的洞察力
  • 能針對未來做出更好的規劃,故你能做出更安全的成長或衰退預測
  • 你可以避免或停止,對你個人或企業關係造成負面影響的行為
  • 系統思考可以讓你針對問題有更清楚地瞭解,做出更好的選擇
  • 你可以做出更精確的預測、更好的假設、偵測問題的瓶頸、尋找問題的成因、測試可能的解決方案、探索你做出的干預手段可能會有什麼長短期影響


  1. 系統的組成元素有:
系統的組成元素
說明
Elements 
  • 系統中的角色 (actors)
Interactions
  • 系統中,actors 間的關係
Functions or purpose
  • Function 用於非人類系統;purpose 用於人類系統
  • 系統元素間 (elements) 互動 (interactions) 的驅動力 (motivator) 與催化劑 (catalyst)


  1. 你的人體循環就是一個系統:
系統的組成元素
說明
Elements 
心臟、肺、血液、血管、動脈、靜脈等
Interactions
在你體內血液、氧氣與其他重要養分的流動
Functions or purpose
循環系統的目的是讓血液、氧氣、養分與賀爾蒙流動全身,觸及你人體內所有細胞,最終的目的是讓你維持生命


  1. 學校也是一個系統:
系統的組成元素
說明
Elements 
學生、老師、教材、學校目標等
Interactions
上課、師生互動、學生間的友誼建立、目標導向等
Functions or purpose
提供良好的課程,培育聰明、能自力更生的年輕人,對整體社會提供正面的貢獻


  1. 系統中的 stocks (庫存) 的特性:
Stocks 特性
說明
可以是實體的
例如銀行帳戶中的錢、企業產品庫存、需要寫進書本中的資訊等
可以是虛擬的
可以是人心中的感受或態度,例如,你對不忠的伴侶、酗酒父母的憤怒程度等
並非靜態的
它會根據 flow 產生的衝擊而改變
是某個時間點的系統快照 (snapshots)
顯示在不斷改變的 flows 狀況下,那時候的系統狀況


  1. Flows 是對系統產生衝擊的行為,例如成功或失敗、購買或銷售、存款或提款、出生或死亡、成長或衰退等。stock 會受到流入 (inflow) 與流出 (outflow) 所影響。真實世界,不會只有如下圖這麼單純,stock 會受到多個 inflows 與 outflows 的影響,我們可以透過干預 inflows 與 outflows 來調整 stock level
  2. 假設 stock 是你的銀行帳戶,inflow 是你的月薪,outflow 是你的每月花費。如果你希望有夠多的 money inflow,你將 stock 部分的金錢放到定存,你的 stock 會透過 reinforcing feedback 影響你的 inflow,當你的帳戶金額越多,你就會放更多的錢到定存帳戶並產生更多的利息,inflow 不斷增加、stock level 也跟著增加,產生正向循環。
  3. 以銀行帳戶為例,看 inflow / outflow / stock 三者間的關係:
inflow / outflow / stock 三者間的關係
說明
如果賺的薪水超過花費的
level of stock (銀行帳戶) 會提升
如果花費超過賺的薪水
level of stock (銀行帳戶) 會銳減,若狀況持續,最終會導致負債
如果賺 100、花 100
level of stock (銀行帳戶) 會持平 (i.e. 動態平衡)
如果你減少花費 (outflow),並尋找額外的收入 (inflow)
你的 level of stock (銀行帳戶) 會更快速上升


  1. 並非所有系統都會擁有 inflow 與 outflow,以非再生天然資源 (non-renewable resources) 為例,例如,新發現的油田,當你開採的速度越快,油田耗盡的速度就會越快。
  2. Stocks 與 flows 的通則 (general rules):
  • Inflows > outflows,level of stock 會上升
  • Outflows > inflows, level of stock 會下降
  • Outflows = inflows,level of stock 會維持在現在的水準且不會改變,這就是所謂的動態平衡 (dynamic equilibrium)
  • 無論是 outflow 減少,或是 inflow 增加,level of stock 都會增加
  • Stocks 扮演系統緩衝 (buffer) 的角色、提供安全屏障,因為 stocks 提供延遲 (delay) 的功能,避免讓系統馬上受到衝擊
  • Stocks 擁有保持 inflows 與 outflows 兩者獨立存在的能力

  1. 我們常有較注意 stocks 而非 flows 得傾向。stocks 相較之下較明顯、可見。即使我們正在觀察 flows,我們也會有自然的傾向,較注意 inflows 而非 outflows。這樣的傾向,有時候會忘記有超過一種以上的方法可以調節 level of stock (減少 outflows 一樣能達到提升 level of stock 的效果)。
  2. 我們可以快速改變 flows,但是 stocks 的反應是相對緩慢的。我們可以吃一條巧克力 (inflow),然後去跑步半小時來消耗多餘的卡路里 (outflow),但是我們的體重 (stock) 並不會立即上升或下降;我們可以在短時間種植一百株樹木 (inflow),但是其需要數十年以後才能變成森林 (stock);全球暖化的現象,也無法馬上反轉。
  3. Stocks 具備緩慢改變的本性,在系統中扮演 buffers、lags 或 delays 的角色。stocks 越大,改變的速度越慢。
  4. 系統動態性 (system dynamics) 顯示複雜系統有非線性行為的傾向。透過 stocks, flows, feedback 與 delays,可以讓我們偵測系統內的改變。stocks 與 flows 是系統動態模型的基礎,構成系統情節的元素有:變數、連結、心智模型、連結的方向、延遲與 feedback loops。

2020/01/31

[Travel] 2020/01 宜蘭

煙波大飯店蘇澳四季雙泉館
煙波大飯店蘇澳四季雙泉館

內埤海灣
內埤海灣

豆腐岬風景區
豆腐岬風景區

粉鳥林
粉鳥林

粉鳥林

蜊埤湖
蜊埤湖

蜊埤湖

2020/01/30

2020/01/10

[Java] [Freemarker] Generate Html file from FTL?

Requirement
I would like to generate HTML file as bellows:
<html>
<body>
    <p>Hello Albert! You have the following messages:
        <li><b>Tim:</b> Please don't forget to bring the laptop!</li>
        <li><b>Cindy:</b> Can you give me a visit this afternoon?</li>
        <li><b>Richard:</b> Don't forget the papers this time!</li>
        <li><b>Mom:</b> Don't forget the milk this time!</li>
        </p>
</body>
</html>

FTL template file
<html>
<body>
    <p>Hello ${name}! You have the following messages:
    <#if messages??>
    <#-- if message has data, get data via iteration
         https://freemarker.apache.org/docs/ref_directive_if.html -->
        <#list messages as m>
        <li><b>${m.from}:</b> ${m.body}</li>
        </#list>
    <#else> <#-- if message is null, then show NO Message Found -->
        No Message Found!
    </#if>
    </p>
</body>
</html>


Test Case
package com.test.batch;

import com.test.batch.exception.FtlException;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.Builder;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class FtlTest {

    private Configuration cfg;

    @Before
    public void init() {
        cfg = new Configuration();
        // 設定到 classpath 讀取 ftl file
        cfg.setClassForTemplateLoading(this.getClass(), "/");
    }

    @Test
    public void testMessageHtml() {
        try (Writer file = new FileWriter(new File("C:/ftl_message.html"));) {
            Template template = cfg.getTemplate("ftl/message.ftl");

            Map<String, Object> data = new HashMap<>();
            data.put("name", "Albert");

            List<Message> messages = new ArrayList<>();
            messages.add(Message.builder().from("Tim").body("Please don't forget to bring the laptop!").build());
            messages.add(Message.builder().from("Cindy").body("Can you give me a visit this afternoon?").build());
            messages.add(Message.builder().from("Richard").body("Don't forget the papers this time!").build());
            messages.add(Message.builder().from("Mom").body("Don't forget the milk this time!").build());

            data.put("messages", messages);

            template.process(data, file);
        } catch (IOException | TemplateException e) {
            throw new FtlException("fail to generate file from ftl file : " + e.getMessage(), e);
        }
    }

    @Getter
    @Builder
    public static class Message {
        private String from;
        private String body;
    }

}




2020/01/09

[IntelliJ] How to show JavaDoc via cursor?

For example:


IDE Configuration
Check "Show quick documentation on mouse move", and click OK to save your configuration.

2020/01/08

[Spring] How to invoke a spring component dynamically

Requirement
Assume I have 2 job classes, I will use command line to execute with job name, and program invoke specific Job class to do something.

Command line looks like:
>java -jar batch-0.0.1-SNAPSHOT.jar -name Job1

The two Job class are as bellows:
package com.test.batch.job;

import com.test.batch.service.Service1;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Slf4j
@Component ("Job1")
public class Job1 implements Job{

    @Autowired
    private Service1 service1;

    public void execute() {
        log.debug("execute job1");
        service1.sayHello();
    }

}



package com.test.batch.job;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

@Component("Job2")
@Slf4j
public class Job2 implements Job {

    @Override
    public void execute() {
        log.debug("execute job2");
    }

}

Invoke specific Job class based on the argument which provide by user:
package com.test.batch.service;

import com.test.batch.job.Job;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class JobExecutor {

    @Autowired
    private ApplicationContext ctx;

    public void execute(String argument) {
        try {
            // get bean by name
            Job job = (Job) ctx.getBean(argument);
            job.execute();
        } catch (Exception e) {
            String error = "Fail to execute batch job, you provide job name is " + argument;
            log.error(error, e);
        }
    }

}


Command line runner class:
package com.test.batch;

import com.beust.jcommander.JCommander;
import com.test.batch.parameter.BatchJobArgument;
import com.test.batch.service.JobExecutor;
import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class BatchCommandLineRunner implements CommandLineRunner {

    @Autowired
    private JobExecutor jobExecutor;

    @Override
    public void run(String... args) throws Exception {
        BatchJobArgument arguments = new BatchJobArgument();

        JCommander jCommander = JCommander.newBuilder().build();
        jCommander.addObject(arguments);
        jCommander.parse(args);

        if(arguments.isHelp()){
            jCommander.usage();
            return;
        }

        log.debug("arguments = {}", arguments);

        if(Strings.isNullOrEmpty(arguments.getName())) {
            log.debug("Please assign job name (ex. -name Job1)");
        }else{
            jobExecutor.execute(arguments.getName());
        }
    }

}