Total Pageviews

2018/12/12

[Spring Boot] An example to build a SOAP Web Service Provider

Here has the steps to build a SOAP web service provider:

Step 1. Go to https://start.spring.io/ download spring boot project

Step 2. Import spring boot project into your eclipse


Step 3. Add web services-related dependency into pom.xml
    <dependency>
        <groupId>wsdl4j</groupId>
        <artifactId>wsdl4j</artifactId>
    </dependency>

    <dependency>
        <groupId>org.apache.ws.xmlschema</groupId>
        <artifactId>xmlschema-core</artifactId>
        <version>2.0.1</version>
    </dependency>

Step 4. Create two XML schema files
countries.xsd
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://ws/oi2/test/country"
    targetNamespace="http://ws/oi2/test/country"
    elementFormDefault="qualified">

    <xs:element name="getCountryRequest">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="name" type="xs:string" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="getCountryResponse">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="country" type="tns:country" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:complexType name="country">
        <xs:sequence>
            <xs:element name="name" type="xs:string" />
            <xs:element name="capital" type="xs:string" />
        </xs:sequence>
    </xs:complexType>
    
</xs:schema>

employees.xsd
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://ws/oi2/test/emp"
    targetNamespace="http://ws/oi2/test/emp"
    elementFormDefault="qualified">

   <xs:element name="getEmployeeByIdRequest">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="id" type="xs:int" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="getEmployeeRequest">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="name" type="xs:string" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="getEmployeeResponse">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="employee" type="tns:employee" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:complexType name="employee">
        <xs:sequence>
            <xs:element name="id" type="xs:int" />
            <xs:element name="name" type="xs:string" />
        </xs:sequence>
    </xs:complexType>
    
</xs:schema>

Step 4. Add plugin into pom.xml to generate domain class from XML schema
    <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>jaxb2-maven-plugin</artifactId>
        <version>1.6</version>
        <executions>
            <execution>
        <id>xjc</id>
        <goals>
            <goal>xjc</goal>
        </goals>
            </execution>
        </executions>
        <configuration>
            <schemaDirectory>${project.basedir}/src/main/resources/xsd</schemaDirectory>
            <outputDirectory>${project.basedir}/src/main/java</outputDirectory>
            <clearOutputDir>false</clearOutputDir>
        </configuration>
    </plugin>

Step 5. execute mvn install to generate domain classes


Step 6. create employee / country repository classes
CountryRepository:
package com.example.soapWsDemo.repository;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.annotation.PostConstruct;

import org.springframework.stereotype.Component;

import ws.oi2.test.country.Country;

@Component
public class CountryRepository {
    
    public List<Country> countries = new ArrayList<>();
    
    @PostConstruct
    public void createCountries() {
        Country taiwan = new Country();
        taiwan.setName("台灣");
        taiwan.setCapital("台北");

        Country japan = new Country();
        japan.setName("日本");
        japan.setCapital("東京");

        Country sKorea = new Country();
        sKorea.setName("韓國");
        sKorea.setCapital("首爾");

        countries = Arrays.asList(taiwan, japan, sKorea);
    }
    
    public Country findCountry(String name) {
        if (name == null || "".equals(name)) {
            throw new IllegalArgumentException("請提供國家名稱");
        }

        Country result = null;
        for (int i = 0; i < countries.size(); i++) {
            Country c = countries.get(i);
            if (c.getName().equals(name)) {
                result = c;
                break;
            }
        }
        
        if(result == null) {
            throw new IllegalArgumentException("查無資料");
        }
        return result;
    }
    
}


EmployeeRepository:

package com.example.soapWsDemo.repository;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.annotation.PostConstruct;

import org.springframework.stereotype.Component;

import ws.oi2.test.emp.Employee;

@Component
public class EmployeeRepository {
    public List<Employee> employees = new ArrayList<>();

    @PostConstruct
    public void createEmployees() {
        Employee albert = new Employee();
        albert.setId(1);
        albert.setName("Albert");

        Employee mandy = new Employee();
        mandy.setId(2);
        mandy.setName("Mandy");

        employees = Arrays.asList(albert, mandy);
    }

    public Employee getEmployee(String name) {
        if (name == null || "".equals(name)) {
            throw new IllegalArgumentException("姓名不可為空");
        }

        Employee emp = null;

        for (int i = 0; i < employees.size(); i++) {
            Employee rs = employees.get(i);
            if (rs.getName().equals(name)) {
                emp = rs;
                break;
            }
        }

        if (emp == null) {
            throw new IllegalArgumentException("查無此人");
        }

        return emp;
    }

    public Employee getEmployee(Integer id) {
        if (id == null) {
            throw new IllegalArgumentException("id 不可為空");
        }

        Employee emp = null;

        for (int i = 0; i < employees.size(); i++) {
            Employee rs = employees.get(i);
            if (rs.getId() == id) {
                emp = rs;
                break;
            }
        }

        if (emp == null) {
            throw new IllegalArgumentException("查無此人");
        }

        return emp;
    }
}


Step 7. Create service endpoint

CountryEndPoint:
package com.example.soapWsDemo.endPoint;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;

import com.example.soapWsDemo.repository.CountryRepository;

import ws.oi2.test.country.GetCountryRequest;
import ws.oi2.test.country.GetCountryResponse;

@Endpoint
public class CountryEndPoint {

    private static final String NAMESPACE_URI = "http://ws/oi2/test/country";

    @Autowired
    private CountryRepository countryRepo;

    @PayloadRoot(namespace = NAMESPACE_URI, localPart = "getCountryRequest")
    @ResponsePayload
    public GetCountryResponse getCountryRequest(@RequestPayload GetCountryRequest request) {
        String name = request.getName();

        GetCountryResponse response = new GetCountryResponse();
        response.setCountry(countryRepo.findCountry(name));

        return response;
    }

}

EmployeeEndPoint:
package com.example.soapWsDemo.endPoint;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.annotation.RequestPayload;
import org.springframework.ws.server.endpoint.annotation.ResponsePayload;

import com.example.soapWsDemo.repository.EmployeeRepository;

import ws.oi2.test.emp.GetEmployeeByIdRequest;
import ws.oi2.test.emp.GetEmployeeRequest;
import ws.oi2.test.emp.GetEmployeeResponse;

@Endpoint
public class EmployeeEndPoint {

    private static final String NAMESPACE_URI = "http://ws/oi2/test/emp";
    
    @Autowired
    private EmployeeRepository empRepo;
    
    @PayloadRoot(namespace = NAMESPACE_URI, localPart = "getEmployeeRequest")
    @ResponsePayload
    public GetEmployeeResponse getEmployee(@RequestPayload GetEmployeeRequest request) {
        String name = request.getName();

        GetEmployeeResponse response = new GetEmployeeResponse();
        response.setEmployee(empRepo.getEmployee(name));

        return response;
    }

    @PayloadRoot(namespace = NAMESPACE_URI, localPart = "getEmployeeByIdRequest")
    @ResponsePayload
    public GetEmployeeResponse getEmployeeByIdRequest(@RequestPayload GetEmployeeByIdRequest request) {
        Integer id = request.getId();

        GetEmployeeResponse response = new GetEmployeeResponse();
        response.setEmployee(empRepo.getEmployee(id));

        return response;
    }
}


Step 8. Create a new class with Spring WS related beans configuration:
package com.example.soapWsDemo.config;

import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.config.annotation.WsConfigurerAdapter;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.xml.xsd.XsdSchemaCollection;
import org.springframework.xml.xsd.commons.CommonsXsdSchemaCollection;

@EnableWs
@Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
    
    @Bean
    public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
        MessageDispatcherServlet servlet = new MessageDispatcherServlet();
        servlet.setApplicationContext(applicationContext);
        servlet.setTransformWsdlLocations(true);
        return new ServletRegistrationBean(servlet, "/ws/*");
    }

    @Bean
    public DefaultWsdl11Definition employees() throws Exception {
        DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();
        wsdl11Definition.setPortTypeName("WebServicePort");
        wsdl11Definition.setLocationUri("/ws");
        wsdl11Definition.setTargetNamespace("http://ws/oi2/test");
        wsdl11Definition.setSchemaCollection(getXsds());
        return wsdl11Definition;
    }

    @Bean
    public XsdSchemaCollection getXsds() throws Exception {
        CommonsXsdSchemaCollection xsds = new CommonsXsdSchemaCollection(new ClassPathResource("/xsd/employees.xsd"),
                new ClassPathResource("/xsd/countries.xsd"));
        xsds.setInline(true);
        return xsds;
    }
    
}



Step 9. launch spring boot application and use SOAP UI to do test




2018/12/11

[SQuirreL SQL Client] How to change font size and font type in SQL entry area ?

Problem
How to change font size and font type in SQuirreL SQL Client's SQL entry area ?

How-To
Step 1. File => New Session Properties




Step 2. Click SQL tab


Step 3. Change font in SQL entry area


Step 4. Close and launch SQuirreL SQL Client



2018/12/10

[Database] [Sybase] datetime-related example

Here has some examples:
-- get current timestamp
select getdate();

-- cast datetime string to datetime
select cast('2018-07-13 23:59:59' as datetime);

-- get year from datetime
select year(getdate());

-- get month from datatime
select month(getdate());

-- get day from datetime
select day(getdate());

-- get datetime string with YYYY-MM-DD hh:mm:ss format
select str_replace(CONVERT (VARCHAR, getdate(), 23), 'T', ' ') ;

-- add 3 days
select dateadd(day, 3, getdate());

-- minus 10 days
select dateadd(day, -10, getdate());

-- add 2 hours
select dateadd(hh, 2, getdate());

-- minus 3 hours
select dateadd(hh, -3, getdate()) 

-- get current datetime and set its time to 0:0:0
select cast(substring(convert(varchar, getdate(), 23), 1, 10) + ' 0:0:0' as datetime);


2018/12/09

[Microsoft Word] 解決打開頁數與實際頁數不符的問題

Version
Office 365

Problem
當我打開一份 Word時,實際頁數是 12 頁,但是打開卻只有 7 頁


How-To
發生的原因,目前還不知道,但是可以透過以下步驟,讓 Word 文件顯示完整頁數
Step 1. 檔案 => 資訊 => 轉換

Step 2. 按下確定,並儲存此文件即可




2018/12/08

[XStram] XStream Annotation Example

Scenario
If I would like to :
- annotate the list to be recognized as an implicit collection
annotate an alias the desired class / fields

How-To
Here has sample code:
@Service
public class XmlService {
    
    public String generateXmlString(Root xmlObject) {
        XStream xstream = new XStream();
        // Process the annotations of the given types and configure the XStream.
        xstream.processAnnotations(new Class[] { Root.class, MyObjectList.class, MyObject.class });
        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xstream.toXML(xmlObject);
        return xml;
    }
    
    // create an alias to the desired class
    @XStreamAlias("MyObject") 
    @Data
    private static class MyObject {
        // ignore fields
    }

    @Data
    private static class MyObjectList {
        // whenever you have a collection which doesn't need to display it's root tag,
        // you can map it as an implicit collection
        @XStreamImplicit
        private List<MyObject> resultObjects;
    }

    @Data
    // create an alias to the desired class
    @XStreamAlias("TEST") 
    private static class Root {
        // create an alias to the field
        @XStreamAlias("MyObjectList") 
        private MyObjectList objectList = null;
    }
}    


The generated XML content looks like:
<?xml version="1.0" encoding="UTF-8"?>
<TEST>
  <MyObjectList>
    <MyObject>
    </MyObject>
    <MyObject>
    </MyObject>
  </MyObjectList>
</TEST>

2018/12/07

[Java] How to zip and extract file by zip4j

Add dependency into your pom.xml
    <dependency>
        <groupId>net.lingala.zip4j</groupId>
        <artifactId>zip4j</artifactId>
        <version>1.3.2</version>
    </dependency>


Here has sample code:
package com.demo.util;

import java.io.File;
import java.util.ArrayList;

import com.google.common.base.Strings;

import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.util.Zip4jConstants;

public class ZipUtils {

    /**
     * 將檔案壓縮成 zip file.
     * 
     * @param files
     *            來源檔案
     * @param dest
     *            路徑 + zip file name
     * @param password
     *            密碼 (optional)
     * @throws ZipException
     *             若壓縮過程發生錯誤,會拋出 ZipException
     */
    public static void addFilesIntoZip(ArrayList<File> files, String dest, String password) throws ZipException {
        try {
            deleteExistingFile(dest);
            ZipFile zipFile = new ZipFile(dest);

            ZipParameters parameters = new ZipParameters();
            parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
            parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);

            if (!Strings.isNullOrEmpty(password)) {
                parameters.setEncryptFiles(true);
                parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_STANDARD);
                parameters.setPassword(password);
            }

            zipFile.addFiles(files, parameters);
        } catch (ZipException e) {
            throw new ZipException("壓縮檔案發生錯誤,錯誤原因: " + e.getMessage(), e);
        }
    }
    
    /**
     * 將某個目錄下的檔案,壓縮成 zip file.
     * 
     * @param sourceDir
     *            來源目錄
     * @param dest
     *            路徑 + zip file name
     * @param password
     *            密碼 (optional)
     * @throws ZipException
     *             若壓縮過程發生錯誤,會拋出 ZipException
     */
    public static void addDirectoryIntoZip(String sourceDir, String dest, String password) throws ZipException {
        try {
            deleteExistingFile(dest);
            ZipFile zipFile = new ZipFile(dest);

            ZipParameters parameters = new ZipParameters();
            parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
            parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);

            if (!Strings.isNullOrEmpty(password)) {
                parameters.setEncryptFiles(true);
                parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_STANDARD);
                parameters.setPassword(password);
            }

            zipFile.addFolder(sourceDir, parameters);
        } catch (ZipException e) {
            throw new ZipException("壓縮檔案發生錯誤,錯誤原因: " + e.getMessage(), e);
        }
    }

    /**
     * 刪除現有檔案.
     * 
     * @param file
     */
    private static void deleteExistingFile(String file) {
        File existingFile = new File(file);
        if (existingFile.exists()) {
            existingFile.delete();
        }
    }

    /**
     * 解壓縮檔案到指定目錄.
     * 
     * @param zip
     *            路徑 + zip file name
     * @param dest
     *            解壓縮到目的地的位置
     * @param password
     *            解壓縮檔案
     * @throws ZipException
     *             若解壓縮過程發生錯誤,會拋出 ZipException
     */
    public static void extractZipFile(String zip, String dest, String password) throws ZipException {
        try {
            ZipFile zipFile = new ZipFile(zip);
            if (zipFile.isEncrypted()) {
                zipFile.setPassword(password);
            }
            zipFile.extractAll(dest);
        } catch (ZipException e) {
            throw new ZipException("解壓縮檔案發生錯誤,錯誤原因: " + e.getMessage(), e);
        }
    }

}


2018/12/06

[閱讀筆記] Value Investing: From Graham to Buffett and Beyond (3/7)


  1. 對投資人來說,投資風險越高,借錢方須負擔的資本成本 (cost of capital) 就越高
  2. 企業真實的 earnings power value 應扣除 cost of capital 與借錢產生的 interest。如調整前的 value 388 million,假設 cost of capital rate 6%,利息為 1 million,調整後的 EPV = 388 - ( 388 * 0.06 ) - 1 = 364 million
  3. 建立顧客忠誠度並不是白吃的午餐,你可能要花費銷售額的 10% 來進行廣告與促銷,若潛在競爭者想要進入市場,你就要花這筆費用 (甚至更多) 當在評估資產的 reproduction costs 時,別忽略了廣告與促銷這兩項費用
  4. 加權平均資金成本(WACC, Weighted average cost of capital) 就是依公司的長期資金來源市價,佔所有長期資金來源的總市價的百分比,加權平均算出公司的平均資金成本。
負債與股東權益
市價
財務結構
資金成本
長期負債 (wb)
4000
40%
6%
特別股
1000
10%
9%
普通股
5000
50%
12%
合計
10000
100%
WACC = 0.4 * 0.06 + 0.1 * 0.09 + 0.5 * 0.12 = 9.3%

  1. 企業的資金來源包含
① 負債 (Debt)
貸款、發行公司債
② 業主權益 (Equity)
發行股票、保留盈餘

  1. 資本結構 (Capital Structure) 代表負債與業主權益的比例 (D/E)。資本結構合理與否,直接影響企業的財務狀況。負債率不應超過50%,長期負債與短期負債比例應適當。
  2. 若某個品牌已經經過多年的廣告、促銷與建立起很好的客戶滿意度,這間企業就擁有很好的進入障礙,確保競爭者很難說服使用者去使用另外一個廠牌的產品 (high switching costs)
  3. 市值(Market Capitalization) 指上市公司普通股的總市值,等於公司最新股價,乘以公司發行在外的總股數。投資圈通常以市值,而非營業額或資產總值,來衡量一家公司的規模。英文常簡稱為 market cap。  

  4. 集中市場在證交所掛牌交易股票的市場叫做集中市場。目前台灣證券交易所都是以電腦搓合的方式交易,但是有些國外交易所還是用人工搓合的方式。
  5. 店頭市場 (OTCOver-The-Counter) 又叫櫃檯交易市場,是提供上櫃股票交易的市場。一般來說比較大型的公司會在集中市場掛牌,比較小的公司會在櫃買市場掛牌。集中市場的大公司,流動性比較好,資訊也比較透明。櫃買市場的小公司,流動性可能稍差,不過如果股市走多頭的時候,小公司漲幅會比大公司還大,不過相對的風險也比較高
  6. 資產需要企業的投資,投資需要獲得回報,這才是正向循環。有獲利的成長 (profitable growth) 指的是營收的成長,超過為了支撐成長所投資的資產的成本。也就是說,無論你投入多少錢建置新廠房,付多少薪資給增加的員工、銷售數字多高,最重要的是你的每股盈餘是否有增加且超過所投入的成本
  7. 葛拉漢的 Net Net 股票投資法 (net net valuation) 不只是便宜的股票,便宜的股票的定義是股價低於內在價值的股票Net net股票則是那種被遺棄十年的股票,而這是一種價值投資方法,其股票估值純粹是根據其流動資產:現金 + 應收帳款 + 存貨 - 債務。事實上,葛拉漢會說這些net net股價幾乎等於清算價值,完全無視廠房、財產與設備 (PPE, Plant, Property and Equipment) 或其他長期資產的價值。葛拉漢所描述的是NCAV(淨流動資產價值, Net Current Asset Value), 你可以看到他談論的不是帳面價值,因為他把無形資產和其他非流動資產假定為0,當人們提到net nets,他們通常意味著 NCAV
  8. 葛拉漢購買NCAV股票的標準是,當股票價格為NCAV2/3時買入。例如:如果每股NCAV10美元,那麼當股票價格為6.66美元時,葛拉漢就會想購買。計算NCAV的公式很簡單,邏輯是找出NCAV高於市場價格的股票:
NCAV =流動資產 – 總負債
計算NCAV的公式很簡單,邏輯是找出NCAV高於市場價格的股票
每股NCAV =(流動資產 – 總負債)/流通在外股數
要獲得每股價值,只需要再除以稀釋後的已發行流通股數
 

  1. “如果你以足夠低價買進一檔股票,通常是公司在業務上有些狀況,讓你能夠有機會以不錯的獲利賣出,儘管公司長期的營運表現可能很糟糕。我稱這種方法可稱為“雪茄屁股”,指的就是在街道上發現只剩一口可抽的雪茄,你沒辦法抽太久,但以“便宜的價格買進”還是會使你獲利。除非你是清算人,否則買進這種企業是愚蠢的。首先,原始“便宜”的價格最終可能已經不便宜。對於處於困境的企業,問題會一個個浮現,廚房通常不會只有一隻蟑螂
  2. 最初你購買所獲得的安全邊際,可能會被公司獲利的低報酬率快速侵蝕。例如,如果你以 800 萬美元買進一間企業,其清算或賣出可賺到 1000 萬美元,那麼你就可以實現高報酬率。但若 10 年內才能以 1000 萬美元賣出,那麼這筆投資就會令人失望,因為公司這 10 年間恐僅能賺取微薄的利潤。時間會是優質公司的朋友,平庸公司的敵人。
  3. 運用 P/B (Price-To-Book Ration, 股價淨值比) 衡量股票價值,還有個科目值得留意:商譽 (Goodwill)。這個因素往往讓帳面價值高度膨脹,使得最昂貴的企業看起來也頗具價值。不幸地,商譽往往只代表併購公司迫不及待要在其他公司出手前,其買下標的公司的慾望,因為標的公司的價值經常被嚴重高估。對於任何企業,商譽如果佔有整體企業帳面價值的很大比率,務必要謹慎對待。對於這些企業的股票,P/B比率可能很低,不過業者一旦認定商譽應該「減值」(換言之,業者承認過去的併購價格過高而決定註銷其價值),帳面價值很大部分可能一夕之間憑空消失。
  4. 市場價值 (market value),即是俗稱的股價,為每天股票市場的行情,由市場供需及對未來前景而定。衡量股票價值的方式有三種:票面價值、帳面價值、市場價值,市價為其中之一
  5. 當一家廠商的 market share 越高,其所創造的規模經濟 (economies of scale) 的優勢就會越顯著。以 microprocessors 來說,其 R&D 的成本非常高昂,但是當你賣出更多的單位,每一個單位的設計成本 (R&D spending) 就會越低。以 Intel AMD 為例,Intel R&D 投入資金比 AMD 多非常多,但是由於銷售量的差異,AMD 的每個銷售單位的研發成本 (R&D per each dollar of sales)反而是 Intel 的兩倍
  1. 無論是哪個領域,銷售的兩大特性:客戶的需求越強勁,每單位的成本就會越低 (more customer demand and lower cost per unit)Microsoft 也是受益於極度規模經濟的優勢,並且以極高的轉換成本 (switching costs) 鎖住客戶
  2. 當商品的利潤越高,商品的價值就越高。超高的利潤就會吸引潛在競爭者進入,其會想辦法拆除進入障礙 (barriers to entry) 或重塑 (reshape) 市場。若想維持超額利潤 (excess returns),企業就必須繼續保護進入障礙,獲取高於資金成本 (cost of capital) 的利潤
  3. 對於有些公司,成長可以創造財富 (Return On Capitals > Cost of Capital )當所賺得錢大於投入的成本,成長越多,賺得就越多,形成正向循環: 
If ROC = 0.2 and R = 0.1
  • 即資金回報的成長超越資金投入的成長
  • F (Growth Factor) = (0.2 - G) / (0.1 - G)
    • 由於 ROC > RF value 永遠大於 1
    • F Value 會隨著成長率的增加而增加
  1. 對於某些公司,成長卻會摧毀他 (Return On Capitals < Cost of Capital )當所賺得錢小於投入的成本,成長越多,賠得就越多,形成惡性循環。沒有競爭優勢、沒有進入障礙的企業,隨著吸引新的進入者進入,形成惡性競爭,導致 rate of return <= cost of return 就是這個類型
If ROC = 0.1and R = 0.12
  • 即資金回報的成長,趕不上資金投入的成長
  • F (Growth Factor) = (0.1 - G) / (0.12 - G)
    • 由於 ROC < R,無論什麼樣的成長率,F value 永遠 < 1
    • 成長率越低, F value 反而 越高
    • 當成長率達到 0.1F value = 0
    • 當成長率越高,摧毀公司的速度越快
  1. 成長需要額外的投資,儘管投資會產生資本回報 (ROC, Return On Capitals)投資需要去借貸、融資,這些就是資金成本 (Cost of Capital)
  2. 身處紅海市場的企業,大部分都是 return on capital (ROC) <= cost of capital (R),成長無法帶來任何價值;只有在市場持續享有競爭優勢、商品有受到進入障礙的保護的企業,才能讓 return on capital (ROC) > cost of capital (R)
  3. 由成長所創造的價值依賴兩個因素
① 遞增的資本投入所創造的獲利率
  • ROC (Return On Capital) 超過 R (Cost Of Capital) 越多,每一塊錢的投資所獲得的回報就越多
  • ROC (Return On Capital) / R (Cost Of Capital)
② 可以投入獲得回報的資本
  • 此因素取決於商品販售成長速度
  • G (Growth) / ROC (Return On Capital)