orm Паттерны

30
ORM Паттерны

Upload: tatum

Post on 17-Jan-2016

77 views

Category:

Documents


0 download

DESCRIPTION

ORM Паттерны. Repository. Repository (хранилище) ― выступает в роли посредника между слоем домена и слоем отображения данных, предоставляя интерфейс в виде коллекции для доступа к объектам домена. Пример. Обратимся к упомянутому ранее примеру на NHibernate. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: ORM Паттерны

ORM Паттерны

Page 2: ORM Паттерны

Repository

Repository (хранилище) ― выступает вроли посредника между слоем домена ислоем отображения данных, предоставляяинтерфейс в виде коллекции для доступа кобъектам домена.

Page 3: ORM Паттерны

Пример

Обратимся к упомянутому ранее примеру на NHibernate.

Пусть у нас определѐн класс автора:public class Author{

public virtual int Id{ get; set; }

public virtual string FirstName{ get; set; }

public virtual string LastName{ get; set; }

public virtual int YearOfBirth{ get; set; }

public virtual Iesi.Collections.Generic.ISet<Book> Books{ get; set; }

}

Page 4: ORM Паттерны

Пример

Файл, который отображает класс автора на таблицу БД:

<class name="Books.Domain.Author, NHibernateDemo" table="Author">

<id name="Id" type="System.Int32" ><column name="Id" not-null="true" /><generator class="identity"/></id>

<property name="FirstName" column="FirstName"/><property name="LastName" column="LastName"/><property name="YearOfBirth" type="System.Int32"

column="YearOfBirth"/>

<set name="Books" table="BookAuthor"><key column="AuthorId"></key><many-to-many class ="Books.Domain.Book, NHibernateDemo"

column="BookId"></many-to-many></set>

</class>

Page 5: ORM Паттерны

Пример

Пусть нам необходимо заполнить выпадающий списокименами авторов, скажем, для того чтобывпоследствии вывести список всех его работ:

public void FillAuthorsComboBox(ComboBox comboBox, ISessionFactory factory){

ISession session = factory.OpenSession();try{

IQuery authorsQuery = session.CreateQuery("FROM Author");

IList<Author> authors = authorsQuery.List<Author>();

foreach (Author author in authors)comboBox.Items.Add(author.LastName + ", " + author.FirstName);

}finally{

session.Close();}

}

Page 6: ORM Паттерны

ПримерПусть одной из функций нашего приложения является выводинформации о всех авторах в формате HTML:

public string GetAllAuthorsAsHTML(ISessionFactory factory) {ISession session = factory.OpenSession();try {

IQuery authorsQuery = session.CreateQuery("FROM Author");IList<Author> authors = authorsQuery.List<Author>();

StringBuilder result = new StringBuilder();result.Append("<HTML>").Append("<BODY>");foreach (Author author in authors){

result.Append("<h2>").Append(author.LastName + ", " + author.FirstName).Append("</h2>");

result.Append("<p>Year of birth: ").Append(author.YearOfBirth.ToString()).Append("</p>");

}result.Append("</BODY>").Append("</HTML>");

return result.ToString();}finally {

session.Close();}

}

Page 7: ORM Паттерны

Пример

Недостатки использованного подхода:

неоправданное дублирование;

зависимость от конкретной реализацииORM;

непрозрачность кода;

невозможность протестировать код.

Page 8: ORM Паттерны

Схема

Page 9: ORM Паттерны

Пример

Определим класс Repository:

public class Repository{

private ISession session;

public Repository(ISession session){

this.session = session;}

public IEnumerable<Author> GetAllAuthors(){

IQuery authorsQuery = session.CreateQuery("FROM Author");return authorsQuery.List<Author>();

}}

Page 10: ORM Паттерны

Пример

Теперь функции, которые работают со спискомавторов, можно переписать следующим образом:

public void FillAuthorsComboBox(ComboBox comboBox,Repository repository)

{IEnumerable<Author> authors = repository.GetAllAuthors();foreach (Author author in authors)

comboBox.Items.Add(author.LastName + ", " + author.FirstName);

}

Page 11: ORM Паттерны

Пример

Функция экспорта в HTML преобразуется следующимобразом:

public string GetAllAuthorsAsHTML(Repository repository){

IEnumerable<Author> authors = repository.GetAllAuthors();

StringBuilder result = new StringBuilder();result.Append("<HTML>").Append("<BODY>");foreach (Author author in authors){

result.Append("<h2>").Append(author.LastName + ", " + author.FirstName).Append("</h2>");

result.Append("<p>Year of birth: ").Append(author.YearOfBirth.ToString()).Append("</p>");

}result.Append("</BODY>").Append("</HTML>");

return result.ToString();}

Page 12: ORM Паттерны

Плюсы

Сокращение дублирования;

прозрачность кода;

возможность создания фиктивногохранилища для упрощения тестирования;

скрытие деталей реализации.

Page 13: ORM Паттерны

Specification

Specification (спецификация) ― паттерн,который инкапсулирует логику отборадоменных объектов в отдельный объект.

Page 14: ORM Паттерны

Specification

Page 15: ORM Паттерны

Specification. Пример

Предположим, что нам необходимо делать выборки авторов,удовлетворяющие разным критериям.

Например, выбирать авторов, родившихся в определѐнный периодили имя которых содержит заданное значение:

public class Repository {public IEnumerable<Author> FindAuthors_BornBetween(int startYear, int endYear) {

return session.QueryOver<Author>().Where(a =>

a.YearOfBirth >= startYear &&a.YearOfBirth <= endYear).List<Author>();

}

public IEnumerable<Author> FindAuthors_NameContains(string value) {return session.QueryOver<Author>()

.Where(a =>a.FirstName.Contains(value)).List<Author>();

}}

Интерфейс класса Repository может стать неоправданно большим.Кроме того, такая реализация нарушает принципоткрытости/закрытости.

Page 16: ORM Паттерны

Specification. Пример

Решением является использование паттерна Спецификация.

Рассмотрим пример применения данного паттерна:

public interface ISpecification<T>{

Expression<Func<T, bool>> IsSatisfiedBy();}

public class Repository{

public IEnumerable<Author> FindAuthors(ISpecification<Author> specification)

{return session.QueryOver<Author>()

.Where(specification.IsSatisfiedBy())

.List<Author>();}

}

Page 17: ORM Паттерны

Specification. Пример

Класс, реализующий интерфейс спецификации ивыполняющий проверку даты рождения на вхождение вопределѐнный диапазон, будет выглядеть так:

public class IsYearOfBirthInRange : ISpecification<Author>{

private int endYear;private int startYear;

public IsYearOfBirthInRange(int startYear, int endYear){

this.startYear = startYear;this.endYear = endYear;

}public Expression<Func<Author, bool>> IsSatisfiedBy(){

return author =>author.YearOfBirth >= startYear &&author.YearOfBirth <= endYear;

}}

Page 18: ORM Паттерны

Specification. Пример

Класс, реализующий интерфейс спецификации ианализирующий имя автора, будет следующим:

public class AuthorNameContains : ISpecification<Author>{

private string value;

public AuthorNameContains(string value){

this.value = value;}

public Expression<Func<Author, bool>> IsSatisfiedBy(){

return author => author.FirstName.Contains(value);}

}

Page 19: ORM Паттерны

Specification. Пример

Рассмотрим теперь пример использования полученныхклассов.

Выведем всех авторов, родившихся во второйполовине XX века:

public void DisplayAuthors(Repository repository){

IEnumerable<Author> authors =repository.FindAuthors(new IsYearOfBirthInRange(1950, 2000));

foreach (Author author in authors)Console.WriteLine(author.FirstName + " " + author.LastName);

}

Page 20: ORM Паттерны

Specification

Для того чтобы сделать выборку, удовлетворяющуюнескольким условиям, можно применить паттерныкомпоновщик и декоратор следующим образом:

Page 21: ORM Паттерны

Specification

Интерфейс спецификации следует расширитьследующим образом:

public interface ISpecification<T>{

Expression<Func<T, bool>> IsSatisfiedBy();

ISpecification<T> Or(ISpecification<T> left);

ISpecification<T> And(ISpecification<T> left);

ISpecification<T> Not();}

Page 22: ORM Паттерны

Specification

Класс составного условия будет следующим:

public abstract class CompositeSpecification<T> : ISpecification<T>{

public abstract Expression<Func<T, bool>> IsSatisfiedBy();

public ISpecification<T> Or(ISpecification<T> right) {return new OrSpecification<T>(this, right);

}

public ISpecification<T> And(ISpecification<T> right) {return new AndSpecification<T>(this, right);

}

public ISpecification<T> Not() {return new NotSpecification<T>(this);

}}

Page 23: ORM Паттерны

Specification

Рассмотрим реализацию наследников упомянутого выше класса.

Класс AndSpecification будет следующим:

public class AndSpecification<T> : CompositeSpecification<T>{

private ISpecification<T> left;private ISpecification<T> right;

public AndSpecification(ISpecification<T> left, ISpecification<T> right){

this.left = left;this.right = right;

}

public override Expression<Func<T, bool>> IsSatisfiedBy(){

return Expression.Lambda<Func<T, bool>>(Expression.And(

left.IsSatisfiedBy(),right.IsSatisfiedBy()));

}}

Page 24: ORM Паттерны

Specification

Класс OrSpecification:

public class OrSpecification<T> : CompositeSpecification<T>{

private ISpecification<T> left;private ISpecification<T> right;

public OrSpecification(ISpecification<T> left, ISpecification<T> right){

this.left = left;this.right = right;

}

public override Expression<Func<T, bool>> IsSatisfiedBy(){

return Expression.Lambda<Func<T, bool>>(Expression.Or(

left.IsSatisfiedBy(),right.IsSatisfiedBy()));

}}

Page 25: ORM Паттерны

Specification

Класс NotSpecification:

public class NotSpecification<T> : CompositeSpecification<T>{

private ISpecification<T> wrapped;

public NotSpecification(ISpecification<T> wrapped){

this.wrapped = wrapped;}

public override Expression<Func<T, bool>> IsSatisfiedBy(){

return Expression.Lambda<Func<T, bool>>(Expression.Not(wrapped.IsSatisfiedBy()));

}}

Page 26: ORM Паттерны

Specification

Теперь классы конкретных условий должнынаследовать класс CompositeSpecification:

public class IsYearOfBirthInRange : CompositeSpecification<Author>{

private int endYear;private int startYear;

public IsYearOfBirthInRange(int startYear, int endYear){

this.startYear = startYear;this.endYear = endYear;

}

public override Expression<Func<Author, bool>> IsSatisfiedBy(){

return author =>author.YearOfBirth >= startYear &&author.YearOfBirth <= endYear;

}}

Page 27: ORM Паттерны

Specification

Рассмотрим пример использования. Выберем всехавторов, которые родились не в первой половине XXвека и имя которых содержит букву «А»:

public static void DisplayAuthors(Repository repository){

ISpecification<Author> condition =((new IsYearOfBirthInRange(1950, 1999)).Not()

).And(

new AuthorNameContains("A"));

IEnumerable<Author> authors = repository.FindAuthors(condition);

foreach (Author author in authors)Console.WriteLine(author.FirstName + " " + author.LastName);

}

Page 28: ORM Паттерны

Specification

Такая возможность языка C# 3.0, как методырасширения позволяет реализовать подобнуюфункциональность следующим образом:

public static class SpecificationUtils{

public static ISpecification<T> Or<T>(this ISpecification<T> left,ISpecification<T> right)

{return new OrSpecification<T>(left, right);

}

public static ISpecification<T> And<T>(this ISpecification<T> left,ISpecification<T> right)

{return new AndSpecification<T>(left, right);

}

public static ISpecification<T> Not<T>(this ISpecification<T> wrapped){

return new NotSpecification<T>(wrapped);}

}

Page 29: ORM Паттерны

Specification

Класс AndSpecification будет выглядеть так:

Остальные подобные классы реализуются аналогично.

public class AndSpecification<T> : ISpecification<T>{

private ISpecification<T> left;private ISpecification<T> right;

public AndSpecification(ISpecification<T> left, ISpecification<T> right) {this.left = left;this.right = right;

}

public Expression<Func<T, bool>> IsSatisfiedBy() {return Expression.Lambda<Func<T, bool>>(

Expression.And(left.IsSatisfiedBy(),right.IsSatisfiedBy()));

}}

Page 30: ORM Паттерны

Specification

Клиентский код остаѐтся без изменений:

public static void DisplayAuthors(Repository repository){

ISpecification<Author> condition =((new IsYearOfBirthInRange(1950, 1999)).Not()

).And(

new AuthorNameContains("A"));

IEnumerable<Author> authors = repository.FindAuthors(condition);

foreach (Author author in authors)Console.WriteLine(author.FirstName + " " + author.LastName);

}