需要一些关于我的软件架构的建议.[代码审查]

Ser*_*pia 7 .net c# architecture

我正在为其他开发人员使用开源C#库.我关注的主要是易用性.这意味着使用直观的名称,直观的方法使用等.

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

我有三个课程: Downloader,Parser和Movie

我当时认为最好只公开我的库的Movie类,并使Downloader和Parser在调用时保持隐藏状态.

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

使用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.
}
Run Code Online (Sandbox Code Playgroud)

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

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

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.
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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        
    }
}
Run Code Online (Sandbox Code Playgroud)

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

你们是否认为我正朝着一条好路走去,或者我是否为自己后来受伤的世界做好准备?

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

感谢阅读和帮助!

还有其他想法吗?

Bry*_*tts 5

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

我建议争取更多基于实例的解耦方法.这自然会将每个操作的定义与其实现分开,为可扩展性和配置留出空间.API的易用性不仅取决于其公共表面,还取决于其适应性.

以下是我如何设计这个系统.首先,定义负责获取电影的内容:

public interface IMovieRepository
{
    Movie FindMovieById(string id);

    Movie FindMovieByTitle(string title);
}
Run Code Online (Sandbox Code Playgroud)

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

public interface IHtmlDownloader
{
    HtmlDocument DownloadHtml(Uri uri);
}
Run Code Online (Sandbox Code Playgroud)

然后,定义使用下载程序的存储库实现:

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...;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

public class NeedsMovies
{
    private readonly IMovieRepository _movies;

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

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

        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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...
}
Run Code Online (Sandbox Code Playgroud)

  • @RCIX:我的目标不仅仅是正确地移动位; 它也是为了尽量减少变化的影响.以这种方式编写的系统(Dependency Inversion/IoC)具有弹性,可以优雅地发展.静态/紧密耦合的API通常不会,尤其是在依赖于第三方服务时.其他开发人员的图书馆保证可持续设计. (2认同)

Jus*_*808 0

我只会公开那些有意义的项目。对于您的代码来说,最终结果是电影信息。下载器和解析器除非用于获取电影信息,否则毫无用处,因此没有理由公开它们。

另外,在您的电影类中,我只会使信息可获取,而不是可设置。该类没有“保存”功能,因此一旦获得信息就没有理由对其进行编辑。

除此之外,如果这是针对其他人的,我会评论每个类、成员以及每个公共/私有类变量的用途。对于 Movie 类,我可能会在类注释中包含一个如何使用它的示例。

最后一件事,如果两个私有类中出现错误,则需要以某种方式通知 Movie 类的用户。可能是一个名为 success 的公共 bool 变量?

根据个人喜好,对于您的电影类,我会让您的两个函数成为构造函数,以便我可以按如下方式构建该类。

电影 myMovie = new Movie("名称"); 或电影 myMovie = 新电影(1245);