AOP 未來想像 on 2023/4/28
AOP future imagination, Aspect, weave, weaving
AOP - Waving 應用
之後
因為真實世界沒有 AOP weaving 語法,故以 Decorator pattern 取代。
於鷹架碼比主程式碼長的狀況下,AOP 的優勢特別明顯。
Task HandleQuery() => CatchHandling(async () =>
{
var qryArgs = new TodoQryAgs
{
Msg = f_testFail ? "測試邏輯失敗" : "今天天氣真好",
Amt = 999
};
dataList = await bizApi.QryDataListAsync(qryArgs);
});
Task HandleAdd() => CatchHandling(async () =>
{
var newTodo = await bizApi.AddFormDataAsync(newTodoDesc);
// Success
dataList.Add(newTodo);
newTodoDesc = string.Empty;
});
//# AOP with Decorator
async Task CatchHandling(Func<Task> action)
{
try
{
f_loading = true;
errMsg = string.Empty;
await action();
}
catch (ApiException ex)
{
if (ex.StatusCode == HttpStatusCode.BadRequest)
{
var msg = await ex.GetContentAsAsync<ErrMsg>();
errMsg = $"ApiException: {msg.Severity}-{msg.Message}";
}
else
{
errMsg = $"ApiException: {ex.Message}";
}
}
catch (Exception ex)
{
errMsg = "EXCEPTION: " + ex.Message;
}
finally
{
f_loading = false;
}
}
之前
async Task HandleQuery()
{
try
{
f_loading = true;
errMsg = string.Empty;
await Task.Delay(500);
var qryArgs = new TodoQryAgs
{
Msg = f_testFail ? "測試邏輯失敗" : "今天天氣真好",
Amt = 999
};
dataList = await bizApi.QryDataListAsync(qryArgs);
}
catch (ApiException ex)
{
if (ex.StatusCode == HttpStatusCode.BadRequest)
{
var msg = await ex.GetContentAsAsync<ErrMsg>();
errMsg = $"ApiException: {msg.Severity}-{msg.Message}";
}
else
{
errMsg = $"ApiException: {ex.Message}";
}
}
catch (Exception ex)
{
errMsg = "EXCEPTION: " + ex.Message;
}
finally
{
f_loading = false;
}
}
async Task HandleAdd()
{
try
{
f_loading = true;
errMsg = string.Empty;
var newTodo = await bizApi.AddFormDataAsync(newTodoDesc);
// Success
dataList.Add(newTodo);
newTodoDesc = string.Empty;
}
catch (ApiException ex)
{
if (ex.StatusCode == HttpStatusCode.BadRequest)
{
var msg = await ex.GetContentAsAsync<ErrMsg>();
errMsg = $"ApiException: {msg.Severity}-{msg.Message}";
}
else
{
errMsg = $"ApiException: {ex.Message}";
}
}
catch (Exception ex)
{
errMsg = "EXCEPTION: " + ex.Message;
}
finally
{
f_loading = false;
}
}
以 Blazor App 為例
未導入AOP
@inject FooFormBiz bizSvc
@injcet IDialogService dlgSvc
@* render dataList *@
@* render formData *@
@code {
List<FooFormData> dataList = new();
FooFormData formData = new();
QryArgs qryArgs = new();
bool f_block = false;
void HandleQryDataList()
{
try {
blockUI();
dataList = bizSvc.QryDataList(args);
// refersh UI
}
catch(Exception ex) {
dlgSvc.ShowAlert(ex.Message);
}
finally {
unblockUI();
}
}
void HandleGetFormData(FormProfle aim)
{
try {
blockUI();
formData = bizSvc.GetFormData(aim);
// refersh UI
}
catch(Exception ex) {
dlgSvc.ShowAlert(ex.Message);
}
finally {
unblockUI();
}
}
void HandleUpdFormData(FormData formData)
{
try {
blockUI();
DoVerify(formData);
bizSvc.UpdFormData(formData);
// refersh UI
}
catch(Exception ex) {
dlgSvc.ShowAlert(ex.Message);
}
finally {
unblockUI();
}
}
void HandleDelFormData(FormProfle aim)
{
try {
blockUI();
bizSvc.DelFormData(aim);
// refersh UI
dataList.RemoveItem(aim);
}
catch(Exception ex) {
dlgSvc.ShowAlert(ex.Message);
}
finally {
unblockUI();
}
}
void HandleAddFormData(FormData formData)
{
try {
blockUI();
DoVerify(formData);
bizSvc.AddFormData(formData);
// refersh UI
dataList.InsertItem(fromData.AsProfile());
}
catch(Exception ex) {
dlgSvc.ShowAlert(ex.Message);
}
finally {
unblockUI();
}
}
void blockUI()
{
f_block = true;
}
void unblockUI()
{
f_block = false;
}
}
導入AOP:CatchHandling
@inject FooFormBiz bizSvc
@injcet IDialogService dlgSvc
@* render dataList *@
@* render formData *@
@code {
List<FooFormData> dataList = new();
FooFormData formData = new();
QryArgs qryArgs = new();
bool f_block = false;
@CatchHandling[]
void HandleQryDataList()
{
dataList = bizSvc.QryDataList(args);
// refersh UI
}
@CatchHandling[]
void HandleGetFormData(FormProfle aim)
{
formData = bizSvc.GetFormData(aim);
// refersh UI
}
@CatchHandling[]
void HandleUpdFormData(FormData formData)
{
DoVerify(formData);
bizSvc.UpdFormData(formData);
// refersh UI
}
@CatchHandling[]
void HandleDelFormData(FormProfle aim)
{
bizSvc.DelFormData(aim);
// refersh UI
dataList.RemoveItem(aim);
}
@CatchHandling[]
void HandleAddFormData(FormData formData)
{
DoVerify(formData);
bizSvc.AddFormData(formData);
// refersh UI
dataList.InsertItem(fromData.AsProfile());
}
void blockUI()
{
f_block = true;
}
void unblockUI()
{
f_block = false;
}
Aspect CatchHandling[]
{
try {
blockUI()
@Target();
}
catch(Exception ex) {
dlgSvc.ShowAlert(ex.Message);
}
finally {
unblockUI()
}
}
}
AOP 語法說明
語法上用符號"@"
表示 wave
。
Aspect
宣示這是 AOP 的 aspect,之後接它的識別名稱,再後面的"[]"
將用來接入參數。
@Target
表示 aspect 欲交織的函式或程式碼區塊,這一個 Aspect 是屬於這個物件專用的。
以 Blazor App 為例二:狀態交換
這個例子我們讓 Aspect 可以共用。我們將把 blockUI、unblockUI 解開。
未導入 AOP
@inject FooFormBiz bizSvc
@injcet IDialogService dlgSvc
@* render formData *@
@code {
FooFormData formData = new();
bool f_block = false;
void HandleSaveFormData(FormData formData)
{
try {
f_block = true;
DoVerify(formData);
bizSvc.SaveFormData(formData);
// refersh UI
}
catch(Exception ex) {
dlgSvc.ShowAlert(ex.Message);
}
finally {
f_block = false;
}
}
}
導入AOP:CatchHandling
@inject FooFormBiz bizSvc
@* render formData *@
@code {
FooFormData formData = new();
bool f_block = false;
@CatchHandling[f_block]
void HandleSaveFormData(FormData formData)
{
DoVerify(formData);
bizSvc.SaveFormData(formData);
// refersh UI
}
}
Aspect CatchHandling[alias bool blocking]
{
@injcet IDialogService dlgSvc
try {
blocking = true;
@Target();
}
catch(Exception ex) {
dlgSvc.ShowAlert(ex.Message);
}
finally {
f_block = false;
}
}
AOP 語法說明
語法上用符號"@"
表示 wave
。
Aspect
宣示這是 AOP 的 aspect,之後接它的識別名稱,再後面的"[]"
將用來接入參數。
@Target
表示 aspect 欲交織的函式或程式碼區塊,這一個 Aspect 是屬於這個物件專用的。
@inject
為 DI, Dependency Injection 依賴注入。 在 Aspect 起始可以注入需要的資源。
alias
是資訊交換的方式用 Call by alias
這古老的方法,在C#用 Call by reference
似乎是等效的?
更多的 AOP 想像
// 可以掛上多個 AOP
@WhenTrue[this.f_isPowerUser]
@IgnoreException[]
@Retry[500,2]
@CatchHandling[f_block]
void HandleSaveFormData(FormData formData)
{
DoVerify(formData);
bizSvc.SaveFormData(formData);
// refersh UI
}
// 可以同時交織 method 與 code block
@WhenTrue[this.f_isPowerUser]
@IgnoreException[]
void HandleSaveFormData(FormData formData)
{
DoVerify(formData);
@Retry[500,2]
@CatchHandling[f_block]
{
bizSvc.SaveFormData(formData);
}
// refersh UI
}
// 資訊交換的方式也可以支援物件的 mutable 特性
Aspect CatchHandling(mutable props := {bool f_loaidng, string errMsg })
{
@inject ActionLogger _logger
try {
props.f_loading = true;
props.errMsg = empty;
@Target();
_logger.Success("Yes");
}
catch(Exception ex) {
props.errMsg = ex.Message;
_logger.Fail(ex.Message);
}
finally {
props.f_loading = false;
}
}
Last updated