Design Pattern:Decorator Pattern 說明

主要概念為 Client 只需要 reference Thing object,可以有多種實作方式,並且定義 Decorator 包裝每一個SpecificThiing 的內容。同時,也可以衍生 SpecifiecDecorator 定義特定要包裝的介面,其他的介面就直接透過 Decorator delegate to SpecificThiing,或者做統一的包裝。

 

透過 Decorator 將每一個特定條件記錄在特定的 class 內,減少 if 判斷,增加理解能力。

舉例如下:

我們有一個 Hat class 定義:

public interface Hat {
    public String getName();
    public int getPrice();
    public String getDescription();
    public boolean isPremium();
}

主要商品為 StandardHat & PreminumHat

public class StandardHat implements Hat {
    private String name;
    public StandardHat(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public int getPrice() {
        return 2000;
    }
    public String getDescription() {
        return getName();
    }
    public boolean isPremium() {
        return false;
    }
}
public class PremiumHat implements Hat {
    private String name;
    public PremiumHat(String name) {
        this.name = name;
    }
 
    public String getName() {
        return name;
    }
 
    public int getPrice() {
        return 3000;
    }
   
    public String getDescription() {
        return getName();
    }
   
    public boolean isPremium() {
        return true;
    }
}

定義 DecoratorHat 處理所有 Decorator 共通的項目(例如回傳名稱):

public abstract class DecoratedHat implements Hat {
 
    private Hat decoratedHat;
 
    protected DecoratedHat(Hat decoratedHat) {
        this.decoratedHat = decoratedHat;
    }
 
    public String getName() {
        return decoratedHat.getName();
    }
 
    protected Hat getDecoratedHat() {
        return decoratedHat;
    }
}

透過 RibbonHat 可以在 Hat 上加裝緞帶:

public class RibbonedHat extends DecoratedHat {
      
    public RibbonedHat(Hat decoratedHat) {
        super(decoratedHat);
    }
   
    public int getPrice() {
        return getDecoratedHat().getPrice() + 100;
    }
   
    public String getDescription() {
        return getDecoratedHat().getDescription() + ", ribboned";
    }
   
    public boolean isPremium() {
        return getDecoratedHat().isPremium();
    }
}

GoldDecorator 塗上金色:

public class GoldenHat extends DecoratedHat {
      
    public GoldenHat(Hat decoratedHat) {
        super(decoratedHat);
    }
   
    public int getPrice() {
        if (isPremium()) {
            return getDecoratedHat().getPrice() + 1000;
        } else {
            return getDecoratedHat().getPrice() + 200;
        }
    }
   
    public String getDescription() {
        return "golden " + getDecoratedHat().getDescription();
    }
   
    public boolean isPremium() {
        return getDecoratedHat().isPremium();
    }
}

Decorator 的使用方式如下:

public void testGoldenRibbonedStandardHat() {
    Hat hat = new RibbonedHat(new GoldenHat(new StandardHat("baseball cap")));
        
    assertThat(hat.getPrice(), is(2300));
    assertThat(hat.getDescription(), is("golden baseball cap, ribboned"));
}

 

解決 windows 10 KB4054517 一直卡在:準備 windows update 請勿關機

最近遇到一個非常麻煩的問題,只要一重新開機,就會進入到藍色的畫面,顯示:【正在準備 windows,請不要關閉電腦】;然後就不會動,要等好幾個小時才會再次進入到 windows 畫面。最氣人的是:當我們以為這樣就已經更新了,其實不然,下次再重新啟動,還是一樣的問題!

查了下這是一個著名的 99% KB4054517 更新問題,會導致 windows 認為可以執行更新,但其實根本無法完成更新檔下載。

解決方案如下:

  • 先刪除 windows modules installer worker,否則無法停用【服務】

  • 停用 windows update 服務:

  • 打開 C 槽,刪除 c:\windows\SoftwareDistribution 中的 DataStore & Download

  • SoftwareDistribution 目錄命名為: SoftwareDistribution.old

 

  • windowsupdate 中下載獨立的更新檔案(以 64BIT 為例),並手動安裝更新

  • 最後不要忘記再次打開 windows update 服務

 

Design Pattern:Adapter Pattern 說明

主要概念:

Client 要使用 CompatibleThing object,但實際的物件介面沒有繼承,因此必須要透過 Adapter 包裝 IncompatibleThing,讓他可以有這樣的介面可以讓 Client 引用。

舉例而言:ShoppingCart 使用 item 項目結帳,但如果我們要直接使用 Hat object 該如何處理?

程式碼如下:

Item Class:

public abstract class Item {
    public abstract String itemName();
    public abstract int itemPrice();
}

Hat Class:

public class Hat { 
    private static final double VAT_PERCENT = 0.2;
    private final String shortName;
    private final String longName;
   
    private final int basePrice;
   
    public Hat(String shortName, String longName, int price) {
        this.shortName = shortName;
        this.longName = longName;
        this.basePrice = price;
    }
   
    public String getShortName() {
        return shortName;
    }
   
    public String getLongName() {
        return longName;
    }
   
    public int getPrice() {
        return (int) (basePrice * (1 + VAT_PERCENT));
    }
}

ShoppingCart Class:

public class ShoppingCart {
   
    private List<Item> items = new LinkedList<>();
   
    public void add(Item item) {
        items.add(item);
    }
   
    public boolean remove(Item item) {
        return items.remove(item);
    }
   
    public long getTotalPrice() {
        long result = 0;
        for (Item item: items) {
            result += item.itemPrice();
        }
        return result;
    }
   
    public String getReceipt() {
        StringBuffer result = new StringBuffer();
        for (Item item: items) {
            result.append(item.itemName() + "\t" + formatMoney(item.itemPrice()) + "\n");
        }
        result.append("----------------------\n");
        result.append("Total:\t\t" + formatMoney(getTotalPrice()) + "\n");
        return result.toString();
    }
   
    private String formatMoney(long money) {
        return "€" + (money / 100) + "." + (money % 100);
    }
}

可以看到 ShoppingCart 只接受 Item object,因此除非 Hat 也要實作 Item 介面,否則不可能傳入到 shoppingCart 中。

解決方案就是建立 Adapter,繼承 Item reference Hat 將他 wrap :

public class HatAdapter extends Item {
   
    private Hat hat;
   
    public HatAdapter(Hat hat) {
        super();
        this.hat = hat;
    }
   
    @Override
    public String itemName() {
        return hat.getShortName();
    }
   
    @Override
    public int itemPrice() {
        return hat.getPrice();
    }
}

如此,就可以將 Hat 傳入到 Adapter ,讓 ShoppingCart 可以直接使用:

public static void main(String[] args) {
     HatAdapter goldenHat = new HatAdapter(new Hat("Golden hat", "Golden hat", 4999));
     HatAdapter pointyHat = new HatAdapter(new Hat("Pointy hat", "Pointy hat", 3000));
     HatAdapter purpleHat = new HatAdapter(new Hat("Purple hat", "Purple hat", 1490));
   
     ShoppingCart cart = new ShoppingCart();
          
     cart.add(goldenHat);
     cart.add(pointyHat);
     cart.add(purpleHat);
          
     cart.remove(pointyHat);
          
     System.out.println(cart.getReceipt());
}

Pattern 架構如下:

新電腦加入舊硬碟方案

最近購買一台新筆電,必須要將舊的 SSD 硬碟加入。

首先需要面對的問題就是要處理磁碟分割,win 10 採用  UEFI,因此都使用 GPT 處理分割,遇到的問題在於會多出一些分割表

其中修復分割區(約300MB)是微軟用來在需要修復系統的情況,供給Windows RE(Windows Recovery Environment)存取之用,這個分割表會變成下圖的【良好(磁碟分割)】:

而這個分割是無法使用上述工具刪除,必須要使用 DISKPART 處理,以下引用 [Win] 刪除修復磁碟分割  圖片說明作法:

請注意這裡的 0, 1, 2 要參考上圖的號碼,否則無法辨識,小心刪除掉目前使用的作業系統磁碟!

接下來就可以初始化磁碟機,同樣採用 GPT 格式建立新的磁碟機了。

SSRS 執行報表發生:使用者資料來源認證錯誤

部屬報表後,若發現以下錯誤:

 

無法完成目前的動作。使用者資料來源認證不符合執行這份報表或共用資料集的需求。可能是使用者資料來源認證未儲存在報表伺服器資料庫中,或使用者資料來源設定為不需要認證卻未指定自動執行帳戶。 (rsInvalidDataSourceCredentialSetting)

這代表報表本身沒有足夠的權限可以存取資料庫。

 

這裡很容易造成誤解,因為我們在 VS 2017 報表中,已經指定資料來源,並且也可以正常的測試運作成功,但部屬之後就會出現上面的問題。

問題的原因應該是部屬時候,並沒有正確的指定報表連線方式,因此預設是不需要認證。

解決方式如下:

  1. 指定報表點選【…】,在 context menu 中點選【管理】

  1. 點選【資料來源】,將之改為【使用下列認證】。並且輸入正確的SQL 登入帳密集可。

可以點選【測試連線】判斷是否輸入正確。

VS 2017 建議安裝擴充套件

Visual Studio 是一個強大的開發工具,尤其套件更是可以簡化開發的時間。建議以下幾個套件可以讓程式開發上更加得心應手。

安裝套件主要是在 Visual Studio Marketplace: Extensions for the Visual Studio family of products

可以直接在 VS 2017 中,點選【工具】->【擴充功能與更新】,再點選【線上】,在搜尋中輸入字串就可自動查詢:

 Add New File

可以直接在畫面上按下 shift + F2 就可以輸入檔案,自動會將檔案放入到指定的目錄中

Code Alignment:

提供快速功能,可以依據不同的特殊字元排序

Shortcuts

Align by… (Dialog) Ctrl + Shift + =
Align by position… (Dialog) Ctrl + =, Ctrl + Backspace
Align by Equals Ctrl + =, Ctrl + =
Align by m_ Ctrl + =, Ctrl + m
Align by Ctrl + =, Ctrl +
Align by . Ctrl + =, Ctrl + .
Align by Space Ctrl + =, Ctrl + Space

VSColorOutput

提供美化 Output 視窗的文字,容易辨識最後結果:

以下幾個是大名鼎鼎的 Productivity Power Tools 2017,但目前提供可以單獨安裝的模組,因此挑選幾個常用的如下:

Github Extension for Visual Studio

必須要用系統管理員執行 VS 2017 後才會生效(因為會修改相關的介面)

安裝完畢後,仍會出現要求安裝 third party Git 套件(此即為 Git windows 官方套件)畫面,直接安裝就可以在 command prompt 使用 git command:

安裝過程中可以點選安裝 unix tool in CMD,如此就可以將 ls 等命令放入到 CMD 中(請注意這會影響到既有的 find command)。

Roslynator 2017

提供 Refactoring 大部分的功能,必須要安裝! A collection of 190+ analyzers and 180+ refactorings for C#, powered by Roslyn.

Viasfora

提供 Keyword Highlight 等等,讓程式碼更加美觀

ZenCoding

快速完成 HTML short cut,例如:  ul>li*3 就會自動產生: <url><li></li>…</ul>

或者直接使用 p + Tab 就可以產生 <p></p>

開發第一張 SQL Server Reporting Service 報表

要透過 Visual Studio 才可以進行報表開發,因此必須要下載與安裝 SSDTSql Server Data Tools,判斷條件很簡單,如果 VS 2017 沒有 BI Template 救代表需要下載了。

 

點選 VS 2017 版本,並且勾選 Sql Server Reporting Services

安裝完畢後,開啟 VS 2017,依據 建立基本資料表報表 (SSRS 教學課程)

基本上依據此份文件教學就可以完成基本第一張報表。

 

簡單說明幾個項目:

  1. 匯入 AdventureWorks2014 資料庫

Sql Server docker image,因此這裡必須要執行 copy host file to docker 方式將下載的資料庫備份匯入:

docker cp .\[host]\[filename] [dockerid]:/[docker]/[folder]

之後就可以用 SSMS 匯入

  1. 報表資料的視窗有時候會不見,這時候可以用 hotkey 快速呼叫(不用到檢視中慢慢找): ctrl+alt+D

  1. 透過部署專案就可以直接發佈到 report server,這裡要說明一下幾個網址的差異

發佈網址(點選 專案->屬性 就可以看到):

可以看到 reportserver 是發佈網址

執行報表網址則是 reports