分享程式代碼相關筆記
目前文章總數:157 篇
最後更新:2024年 12月 07日
1.LanguageController : 中翻英控制器,API接口
2. LanguageService : 中翻英翻譯實作
3. ILanguageService : 中翻英翻譯實作Interface
4. Program : 註冊接口與實例的位置
當GetLanaguage被呼叫時會執行GetCorrespondMessage(message)
此時message的內容會從中文轉成英文
[ApiController]
[Route("[controller]")]
public class LanguageController : ControllerBase
{
private readonly ILogger<LanguageController> _logger;
private readonly ILanguageService _languageService;
public LanguageController(ILogger<LanguageController> logger, ILanguageService languageService)
{
_logger = logger;
_languageService = languageService;
}
[HttpGet(Name = "GetLanaguage")]
public string GetLanaguage(string message)
{
return _languageService.GetCorrespondMessage(message);
}
}
這邊只是為了示範,簡單示意。本篇目的是模組化,而非翻譯實作。
哈囉 -> Hello 假設翻譯成英文
public class LanguageService : ILanguageService
{
private readonly ILogger<LanguageService> _logger;
private readonly IServiceProvider _serviceProvider;
public LanguageService(IServiceProvider serviceProvider, ILogger<LanguageService> logger)
{
_serviceProvider = serviceProvider;
_logger = logger;
}
public string GetCorrespondMessage(string message)
{
if (message == "哈囉")
return "Hello";
return String.Empty;
}
}
接口相當簡單GetCorrespondMessage
public interface ILanguageService
{
string GetCorrespondMessage(string message);
}
.net Core 6 執行建置後,預設API專案會有Swagger
這段代碼假設今天要同時翻譯日語或中國語,那麼會改到以下區塊的代碼,違反OCP原則
並且外層呼叫GetCorrespondMessage時會呼叫多次。
public string GetCorrespondMessage(string message, string nationCode)
{
if(nationCode == "美國")
if (message == "哈囉")
return "Hello";
else if (nationCode == "中國")
if (message == "哈囉")
return "哈啰";
else if (input == "日本")
if (message == "哈囉")
return "こんにちは";
return String.Empty;
}
為了解決第一部分最後的缺點,我們先看改變後的架構,
1. LanguageController:API層依照需求調整,收集所有國家資料
2. Nation:實例的國家,解耦成美國、中國、日本
3. LanguageService:入口,由此收集所有國家的翻譯內容(未來也不用調整此代碼)
4. Interface Nation:一對一實例,並且每個接口繼承 INationBase
5. ILanguageService:入口接口
6. INationBase:提供所有國家相同的行為接口
7. Program:註冊此次的改動
當GetLanaguage被呼叫時會執行GetAllMessage(message)
並且將回傳改為List
[ApiController]
[Route("[controller]")]
public class LanguageController : ControllerBase
{
private readonly ILogger<LanguageController> _logger;
private readonly ILanguageService _languageService;
public LanguageController(ILogger<LanguageController> logger, ILanguageService languageService)
{
_logger = logger;
_languageService = languageService;
}
[HttpGet(Name = "GetLanaguage")]
public List<string> GetLanaguage(string message)
{
return _languageService.GetAllMessage(message);
}
}
新建一個資料夾Nation,裡面是所有國家的實例,未來新增國家語言只需新增,不用修改
1. 下面這段是美國的,繼承 INationAmericaService
public class NationAmericaService : INationAmericaService
{
private readonly ILogger<NationAmericaService> _logger;
public NationAmericaService(ILogger<NationAmericaService> logger)
{
_logger = logger;
}
public string GetCorrespondMessage(string input)
{
#region 此段應實作引用庫,轉成對應文化語言,這邊只是舉例
if (input == "哈囉")
return "Hello";
#endregion
return string.Empty;
}
}
2. 下面這段是中國的,繼承 INationChinaService
如果中國有特殊邏輯,也不會改到其他國家代碼
public class NationChinaService : INationChinaService
{
private readonly ILogger<NationChinaService> _logger;
public NationChinaService(ILogger<NationChinaService> logger)
{
_logger = logger;
}
public string GetCorrespondMessage(string input)
{
#region 此段應實作引用庫,轉成對應文化語言,這邊只是舉例
if (input == "哈囉")
return "哈啰";
#endregion
#region 假設這個文化有些文字要過濾
if (input == "辱華文字")
return string.Empty;
#endregion
return string.Empty;
}
}
3. 下面這段是日本的,繼承 INationJapanService
public class NationJapanService : INationJapanService
{
private readonly ILogger<NationJapanService> _logger;
public NationJapanService(ILogger<NationJapanService> logger)
{
_logger = logger;
}
public string GetCorrespondMessage(string input)
{
#region 此段應實作引用庫,轉成對應文化語言,這邊只是舉例
if (input == "哈囉")
return "こんにちは";
#endregion
return string.Empty;
}
}
1.實做了GetAllMessage
GetNationSericeMapDic() 會依照反射取得INationBase 對照到真正的實例
public List<string> GetAllMessage(string message)
{
var resultMessages = new List<string>();
//取得所有國家
var nationServices = GetNationSericeMapDic();
foreach (var service in nationServices)
{
resultMessages.Add(service.GetCorrespondMessage(message));
}
return resultMessages;
}
2. 核心,實際上是將所有INationBase繼承的實例取出,以後新增的國家只要接口繼承了INationBase,會自動被找出來呼叫
private List<INationBase> GetNationSericeMapDic()
{
//反射取出所有引用INationBase的實例
var nationSericeItems = new List<INationBase>();
var types = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.GetInterfaces().Contains(typeof(INationBase)));
foreach (var type in types)
{
var service = _serviceProvider.CreateScope().ServiceProvider.GetService(type) as INationBase;
if (service != null)
{
nationSericeItems.Add(service);
}
}
return nationSericeItems;
}
1.所有國家都繼承了 INationBase,使其反射的時候可以集中收集關聯性,真正的接口由 INationBase 決定
美國的接口繼承 INationBase
public interface INationAmericaService : INationBase
{
}
2.中國的接口繼承 INationBase
public interface INationChinaService : INationBase
{
}
3.日本的接口繼承 INationBase
public interface INationJapanService : INationBase
{
}
依照需求改成收集所有翻譯內容
public interface ILanguageService
{
public List<string> GetAllMessage(string message);
}
由此決定所有國家翻譯都該執行的行為
public interface INationBase
{
public string GetCorrespondMessage(string input);
}
如果有反射的實作,其實此段可以忽略,但為了說明,仍有處理此端。
為何反射可以成功? 是因為在region的地方 [注入Interface對應實例]
每個INationXXXX由於都繼承INationBase了,因此真正的對照會是在註冊這段。
在反射時雖然都是INationBase,但註冊時決定了哪個國家接口對到了哪個實例。
using InjectReflectionForTranslateLanguageExample.Implement;
using InjectReflectionForTranslateLanguageExample.Implement.Nation;
using InjectReflectionForTranslateLanguageExample.Interface;
using InjectReflectionForTranslateLanguageExample.Interface.Nation;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
#region 注入Interface對應實例
builder.Services.AddScoped<ILanguageService, LanguageService>();
builder.Services.AddScoped<INationAmericaService, NationAmericaService>();
builder.Services.AddScoped<INationChinaService, NationChinaService>();
builder.Services.AddScoped<INationJapanService, NationJapanService>();
builder.Services.AddScoped<INationGermanyService, NationGermanyService>();
#endregion
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthorization();
app.MapControllers();
app.Run();
透過Debug偵錯,可以發現取得的實例
最後每個執行的結果都在 LanguageService 收集回傳給Controller層
1.NationGermanyService:增加德國翻譯實例,繼承INationGermanyService:
2.INationGermanyService:德國接口,繼承: INationBase
此段代碼完成德國翻譯實作,繼承了INationGermanyService
public class NationGermanyService : INationGermanyService
{
private readonly ILogger<NationGermanyService> _logger;
public NationGermanyService(ILogger<NationGermanyService> logger)
{
_logger = logger;
}
public string GetCorrespondMessage(string input)
{
#region 此段應實作引用庫,轉成對應文化語言,這邊只是舉例
if (input == "哈囉")
return "Hallo";
#endregion
return string.Empty;
}
}
代碼也是繼承 INationBase,真正的接口在 INationBase
public interface INationGermanyService : INationBase
{
}
最後可以發現,我們只做了”新增”兩個檔案 INationGermanyService 與 NationGermanyService
過程中不改動原本的代碼,完成模組化,達成OCP原則
未來接手的工程師,不用怕加新的需求