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类.
感谢阅读和帮助!
还有其他想法吗?
您的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)
我只会公开那些有意义的项目。对于您的代码来说,最终结果是电影信息。下载器和解析器除非用于获取电影信息,否则毫无用处,因此没有理由公开它们。
另外,在您的电影类中,我只会使信息可获取,而不是可设置。该类没有“保存”功能,因此一旦获得信息就没有理由对其进行编辑。
除此之外,如果这是针对其他人的,我会评论每个类、成员以及每个公共/私有类变量的用途。对于 Movie 类,我可能会在类注释中包含一个如何使用它的示例。
最后一件事,如果两个私有类中出现错误,则需要以某种方式通知 Movie 类的用户。可能是一个名为 success 的公共 bool 变量?
根据个人喜好,对于您的电影类,我会让您的两个函数成为构造函数,以便我可以按如下方式构建该类。
电影 myMovie = new Movie("名称"); 或电影 myMovie = 新电影(1245);