iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 4
2

今天更新一個版本,主要處理上一版原先寫法問題:

XML存取實作IBlogService介面,MSSQL存取也實作IBlogService介面,導致"重複編寫相同方法"

舉例

GetPosts都是從cache讀取資料,實作代碼也一樣。
假如後面新增一個SQLite讀取方式,代表程式要重複三次一樣的代碼,造成之後不好統一維護問題

    public interface IBlogService  
    {  
        //略...  
        Task<IEnumerable<Post>> GetPosts(int count, int skip = 0);  
        //略...  
    }  
      
    public class FileBlogService : IBlogService{  
        //略...  
        public virtual Task<IEnumerable<Post>> GetPosts(int count, int skip = 0)  
        {  
            //略...  
            var posts = _cache  
                .Where(p => p.PubDate <= DateTime.UtcNow && (p.IsPublished || isAdmin))  
                .Skip(skip)  
                .Take(count);  
            //略...  
        }   
        //略...  
    }  
      
    public class MSSqlBlogService : IBlogService{  
        //略...  
        public virtual Task<IEnumerable<Post>> GetPosts(int count, int skip = 0)  
        {  
            //略...  
            var posts = _cache  
                .Where(p => p.PubDate <= DateTime.UtcNow && (p.IsPublished || isAdmin))  
                .Skip(skip)  
                .Take(count);  
            //略...  
        }   
        //略...  
    }      

解決方式

避免重複方法可以使用繼承抽象類別

舉例

1.新增一個抽象類別InMemoryBlogServiceBase實作IBlogService介面
2.把GetPosts實作代碼寫在該類別
3.接著FileBlogService跟MSSqlBlogService的GetPosts方法都刪除
4.FileBlogService跟MSSqlBlogService繼承該類別

public interface IBlogService  
{  
    //略...  
    Task<IEnumerable<Post>> GetPosts(int count, int skip = 0);  
    //略...  
}  
  
public abstract class InMemoryBlogServiceBase : IBlogService{  
    //略...  
    public virtual Task<IEnumerable<Post>> GetPosts(int count, int skip = 0)  
    {  
        //略...  
        var posts = _cache  
            .Where(p => p.PubDate <= DateTime.UtcNow && (p.IsPublished || isAdmin))  
            .Skip(skip)  
            .Take(count);  
        //略...  
    }   
    //略...  
}  
  
public class FileBlogService : InMemoryBlogServiceBase{  
    //略...  
}  
  
public class MSSqlBlogService : InMemoryBlogServiceBase{  
    //略...  
}     

延伸

聰明的讀者這時候會想到"不一定需要抽象類別",繼承普通類別也可以達到同樣效果

是的,但是這對同樣實作代碼的方法有用而已,假如有不同行為又必須實作的方法就沒有約束效果

舉例

FileBlogService跟MSSqlBlogService在保存資料都需要使用SavePost方法,但是有不同的行為:一個保存在XML,一個保存在DB。

這時候可以在InMemoryBlogServiceBase建立一個抽象方法SavePost,要求繼承的類別一定要實作方法,否則無法編譯(如圖)。

代碼:

public interface IBlogService  
{  
    //略...  
    Task SavePost(Post post);  
    //略...  
}  
  
public abstract class InMemoryBlogServiceBase : IBlogService{  
    //略...  
    public abstract Task SavePost(Post post);  
    //略...  
}  
  
public class FileBlogService : InMemoryBlogServiceBase{  
    public override async Task SavePost(Post post){  
        //保存資料到XML  
    }  
}  
  
public class MSSqlBlogService : InMemoryBlogServiceBase{  
    public override async Task SavePost(Post post){  
        //保存資料到DB  
    }  
}     

結論

做完以上的動作後,當前端更新文章資料傳到BlogController
藉著IBlogService介面物件呼叫SavePost方法

public class BlogController : Controller  
{  
    private readonly IBlogService _blog;  
    public BlogController(IBlogService blog, IOptionsSnapshot<BlogSettings> settings, WebManifest manifest)  
    {  
        _blog = blog;  
    }  
    public async Task<IActionResult> UpdatePost(Post post)  
    {  
        await _blog.SavePost(existing);  
    }      
}  

只要替換ConfigureServices方法的依賴注射類別:
達到改一行代碼,就可以替換整個存取邏輯。

services.AddSingleton<IBlogService, 想要存取方式的類別>(); 

上一篇
03.支援 SQL Server
下一篇
05.支持MarkDown編輯發文
系列文
輕量高效.NET Core開源Blog引擎:Miniblog.Core30

尚未有邦友留言

立即登入留言