需要一些关于我的软件架构的建议.[代码审查] [英] Need some suggestions on my softwares architecture. [Code review]

查看:24
本文介绍了需要一些关于我的软件架构的建议.[代码审查]的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在制作一个开源 C# 库供其他开发人员使用.我最关心的是易用性.这意味着使用直观的名称、直观的方法用法等.

I'm making an open source C# library for other developers to use. My key concern is ease of use. This means using intuitive names, intuitive method usage and such.

这是我第一次为其他人做一些事情,所以我真的很关心架构的质量.另外,我不介意学习一两件事.:)

This is the first time I've done something with other people in mind, so I'm really concerned about the quality of the architecture. Plus, I wouldn't mind learning a thing or two. :)

我有三个类:下载器、解析器和电影

我认为最好只公开我的库的 Movie 类,并让下载器和解析器保持隐藏状态,不被调用.

I was thinking that it would be best to only expose the Movie class of my library and have Downloader and Parser remain hidden from invocation.

最终,我看到我的图书馆被这样使用.

Ultimately, I see my library being used like this.

使用 FreeIMDB;

using FreeIMDB;

public void Test()
{
    var MyMovie = Movie.FindMovie("The Matrix");
    //Now MyMovie would have all it's fields set and ready for the big show.
}

您能否回顾一下我是如何计划的,并指出我做出的任何错误判断以及我可以改进的地方.

Can you review how I'm planning this, and point out any wrong judgement calls I've made and where I could improve.

记住,我主要关心的是易用性.

Movie.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;


namespace FreeIMDB
{
    public class Movie
    {
        public Image Poster { get; set; }
        public string Title { get; set; }
        public DateTime ReleaseDate { get; set; }
        public string Rating { get; set; }
        public string Director { get; set; }
        public List<string> Writers { get; set; }
        public List<string> Genres { get; set; }
        public string Tagline { get; set; }
        public string Plot { get; set; }
        public List<string> Cast { get; set; }
        public string Runtime { get; set; }
        public string Country { get; set; }
        public string Language { get; set; }

        public Movie FindMovie(string Title)
        {
            Movie film = new Movie();
            Parser parser = Parser.FromMovieTitle(Title);

            film.Poster = parser.Poster();
            film.Title = parser.Title();
            film.ReleaseDate = parser.ReleaseDate();
            //And so an so forth.
        }

        public Movie FindKnownMovie(string ID)
        {
            Movie film = new Movie();
            Parser parser = Parser.FromMovieID(ID);

            film.Poster = parser.Poster();
            film.Title = parser.Title();
            film.ReleaseDate = parser.ReleaseDate();
            //And so an so forth.
        }
    }
}

Parser.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HtmlAgilityPack;

namespace FreeIMDB
{
    /// <summary>
    /// Provides a simple, and intuitive way for searching for movies and actors on IMDB.
    /// </summary>
    class Parser
    {
        private Downloader downloader = new Downloader();                
        private HtmlDocument Page;

        #region "Page Loader Events"
        private Parser()
        {

        }

        public static Parser FromMovieTitle(string MovieTitle)
        {
            var newParser = new Parser();
            newParser.Page = newParser.downloader.FindMovie(MovieTitle);
            return newParser;
        }

        public static Parser FromActorName(string ActorName)
        {
            var newParser = new Parser();
            newParser.Page = newParser.downloader.FindActor(ActorName);
            return newParser;
        }

        public static Parser FromMovieID(string MovieID)
        {
            var newParser = new Parser();
            newParser.Page = newParser.downloader.FindKnownMovie(MovieID);
            return newParser;
        }

        public static Parser FromActorID(string ActorID)
        {
            var newParser = new Parser();
            newParser.Page = newParser.downloader.FindKnownActor(ActorID);
            return newParser;
        }
        #endregion

        #region "Page Parsing Methods"
        public string Poster()
        {
            //Logic to scrape the Poster URL from the Page element of this.
            return null;
        }

        public string Title()
        {
            return null;
        }

        public DateTime ReleaseDate()
        {
            return null;
        }        
        #endregion        
    }
}

-----------------------------------------------

你们认为我是在朝着好的道路前进,还是我会让自己在以后的世界里受到伤害?

-----------------------------------------------

Do you guys think I'm heading towards a good path, or am I setting myself up for a world of hurt later on?

我最初的想法是将下载、解析和实际填充分开,以便轻松拥有一个可扩展的库.想象一下,如果有一天网站改变了它的 HTML,那么我只需要修改解析类,而无需触及 Downloader.cs 或 Movie.cs 类.

My original thought was to separate the downloading, the parsing and the actual populating to easily have an extensible library. Imagine if one day the website changed its HTML, I would then only have to modifiy the parsing class without touching the Downloader.cs or Movie.cs class.

感谢阅读和帮助!

还有其他想法吗?

推荐答案

您的 API 大部分是静态的,这意味着您要为将来的可维护性问题做好准备.这是因为静态方法实际上是单例的,有一些明显的缺点.

Your API is mostly static, meaning you are setting yourself up for maintainability issues in the future. This is because the static methods are actually singletons, which have some significant drawbacks.

我建议努力采用更加基于实例、解耦的方法.这自然会将每个操作的定义与其实现分开,为可扩展性和配置留出空间.API 的易用性不仅体现在它的公开表面上,还体现在它的适应性上.

I suggest striving for a more instance-based, decoupled approach. This will naturally separate the definition of each operation from its implementation, leaving room for extensibility and configuration. An API's ease-of-use is measured not only by its public surface, but also by its adaptability.

这是我将如何设计这个系统.首先,定义一些负责获取电影的东西:

Here is how I would go about designing this system. First, define something which is responsible for fetching movies:

public interface IMovieRepository
{
    Movie FindMovieById(string id);

    Movie FindMovieByTitle(string title);
}

接下来,定义负责下载 HTML 文档的内容:

Next, define something which is responsible for downloading HTML documents:

public interface IHtmlDownloader
{
    HtmlDocument DownloadHtml(Uri uri);
}

然后,定义一个使用下载器的存储库实现:

Then, define a repository implementation which uses a downloader:

public class MovieRepository : IMovieRepository
{
    private readonly IHtmlDownloader _downloader;

    public MovieRepository(IHtmlDownloader downloader)
    {
        _downloader = downloader;
    }

    public Movie FindMovieById(string id)
    {
        var idUri = ...build URI...;

        var html = _downloader.DownloadHtml(idUri);

        return ...parse ID HTML...;
    }

    public Movie FindMovieByTitle(string title)
    {
        var titleUri = ...build URI...;

        var html = _downloader.DownloadHtml(titleUri);

        return ...parse title HTML...;
    }
}

现在,在任何需要下载电影的地方,您都可以单独依赖 IMovieRepository,而无需直接耦合到它下面的所有实现细节:

Now, anywhere you need to download movies, you can depend solely on IMovieRepository without being directly coupled to all the implementation details beneath it:

public class NeedsMovies
{
    private readonly IMovieRepository _movies;

    public NeedsMovies(IMovieRepository movies)
    {
        _movies = movies;
    }

    public void DoStuffWithMovie(string title)
    {
        var movie = _movies.FindMovieByTitle(title);

        ...
    }
}

此外,您现在可以轻松测试解析逻辑,而无需进行网络调用.只需保存 HTML 并创建一个下载器,将其提供给存储库:

In addition, you can now easily test the parsing logic without having to make web calls. Simply save the HTML and create a downloader which gives it to a repository:

public class TitleHtmlDownloader : IHtmlDownloader
{
    public HtmlDocument DownloadHtml(Uri uri)
    {
        return ...create document from saved HTML...
    }
}

[Test]
public void ParseTitle()
{
    var movies = new MovieRepository(new TitleHtmlDownloader());

    var movie = movies.GetByTitle("The Matrix");

    Assert.AreEqual("The Matrix", movie.Title);

    ...assert other values from the HTML...
}

这篇关于需要一些关于我的软件架构的建议.[代码审查]的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆