Студопедия
Случайная страница | ТОМ-1 | ТОМ-2 | ТОМ-3
АрхитектураБиологияГеографияДругоеИностранные языки
ИнформатикаИсторияКультураЛитератураМатематика
МедицинаМеханикаОбразованиеОхрана трудаПедагогика
ПолитикаПравоПрограммированиеПсихологияРелигия
СоциологияСпортСтроительствоФизикаФилософия
ФинансыХимияЭкологияЭкономикаЭлектроника

Паттерны проектирования 8 страница



 

Структура

Two ways of implementation

 

There are two ways to implement this design pattern.

User knows the servant (in which case he doesn't need to know the serviced classes) and sends messages with his requests to the servant instances, passing the serviced objects as parameters.

Serviced instances know the servant and the user sends them messages with his requests (in which case she doesn't have to know the servant). The serviced instances then send messages to the instances of servant, asking for service.

The serviced classes (geometric objects from our example) don't know about servant, but they implement the "IServiced" interface. The user class just calls the method of servant and passes serviced objects as parameters. This situation is shown on figure 1.

User uses servant to achieve some functionality and passes the serviced objects as parameters.

On figure 2 is shown opposite situation, where user don't know about servant class and calls directly serviced classes. Serviced classes then asks servant themselves to achieve desired functionality.

 

User requests operations from serviced instances, which then asks servant to do it for them.

 

PeajiHsaijH

Analyze what behavior servant should take care of. State what methods servant will define and what these methods will need from serviced parameter. By other words, what serviced instance must provide, so that servants methods can achieve their goals.

Analyze what abilities serviced classes must have, so they can be properly serviced. We define an interface, which will enforce implementation of declared methods.

Define an interface specifying requested behavior of serviced objects. If some instance wants to be served by servant, it must implement this interface.

 

Define (or acquire somehow) specified servant (his class).

 

Implement defined interface with serviced classes.

 

пример реализации

// Servant class, offering its functionality to classes implementing // Movable Interface public class MoveServant {

// Method, which will move Movable implementing class to position where public void moveTo(Movable serviced, Position where) {

// Do some other stuff to ensure it moves smoothly and nicely, this is // the place to offer the functionality serviced.setPosition(where);

}

// Method, which will move Movable implementing class by dx and dy public void moveBy(Movable serviced, int dx, int dy) {

// this is the place to offer the functionality

dx += serviced.getPositionQ.xPosition;

dy += serviced.getPosition().yPosition;

serviced.setPosition(new Position(dx, dy));

}

}

// Interface specifying what serviced classes needs to implement, to be // serviced by servant. public interface Movable {

public void setPosition(Position p);


Position getPosition();

 

// One of geometric classes

public class Triangle implements Movable {

// Position of the geometric object on some canvas

private Position p;

// Method, which sets position of geometric object public void setPosition(Position p) { this.p = p;

}

// Method, which returns position of geometric object public Position getPosition() { return this.p;

}

}

// One of geometric classes

public class Ellipse implements Movable {

// Position of the geometric object on some canvas

private Position p;

// Method, which sets position of geometric object public void setPosition(Position p) { this.p = p;

}

// Method, which returns position of geometric object public Position getPosition() { return this.p;

}

}

// One of geometric classes

public class Rectangle implements Movable {

// Position of the geometric object on some canvas

private Position p;

// Method, which sets position of geometric object public void setPosition(Position p) { this.p = p;

}


Me LlioUj WlliCII


positio


public Position getPosition() return this.p;

}


{


}

// Just a very simple container class for position, public class Position {

public int xPosition;

public int yPosition;

public Position(int dx, int dy) { xPosition = dx; yPosition = dy;


Specification (Specification)

In computer programming, the specification pattern is a particular software design pattern, whereby business rules can be recombined by chaining the business rules together using boolean logic.



A specification pattern outlines a business rule that is combinable with other business rules. In this pattern, a unit of business logic inherits its functionality from the abstract aggregate Composite Specification class. The Composite Specification class has one function called IsSatisfiedBy that returns a boolean value. After instantiation, the specification is "chained" with other specifications, making new specifications easily maintainable, yet highly customizable business logic. Furthermore upon instantiation the business logic may, through method invocation or inversion of control, have its state altered in order to become a delegate of other classes such as a persistence repository.

 

Структ



Пример реализации

public interface ISpecification<TEntity> {

bool IsSatisfiedBy(TEntity entity);

}


internal class AndSpecification<TEntity>: ISpecification<TEntity>

{

private ISpecification<TEntity> Specl; private ISpecification<TEntity> Spec2;

internal AndSpecification(ISpecification<TEntity> si, ISpecification<TEntity> s2)


Specl Spec2


si; s2;


 

c bool IsSatisfiedBy(TEntity candidate)

"eturn Specl.IsSatisfiedBy(candidate) && Spec2.IsSatisfiedBy(candidate);

 

}

internal class OrSpecification<TEntity>: ISpecification<TEntity>


private ISpecification<TEntity> Spec1; private ISpecification<TEntity> Spec2;

internal OrSpecification(ISpecification<TEntity> s1, ISpecification<TEntity> s2) {

Spec1 = s1; Spec2 = s2;

}

public bool IsSatisfiedBy(TEntity candidate) {

return Spec1.IsSatisfiedBy(candidate) || Spec2.IsSatisfiedBy(candidate);

}

}

internal class NotSpecification<TEntity>: ISpecification<TEntity>

{

private ISpecification<TEntity> Wrapped;

internal NotSpecification(ISpecification<TEntity> x) {

Wrapped = x;

}

public bool IsSatisfiedBy(TEntity candidate) {

return!Wrapped.IsSatisfiedBy(candidate);

}

}

public static class ExtensionMethods

{

public static ISpecification<TEntity> And<TEntity>(this ISpecification<TEntity> s1, ISpecification<TEntity> s2) {

return new AndSpecification<TEntity>(s1, s2);

}

public static ISpecification<TEntity> Or<TEntity>(this ISpecification<TEntity> s1, ISpecification<TEntity> s2) {

return new OrSpecification<TEntity>(s1, s2);

}

public static ISpecification<TEntity> Not<TEntity>(this ISpecification<TEntity> s) {

return new NotSpecification<TEntity>(s);

}

}

 

 

Пример использования

In this example, we are retrieving invoices and sending them to a collection agency if they are overdue, notices have been sent and they are not already with the collection agency.

We previously defined an OverdueSpecification class that it is satisfied when an invoice's due date is 30 days or older, a NoticeSentSpecification class that is satisfied when three notices have been sent to the customer, and an InCollectionSpecification class that is satisfied when an invoice has already been sent to the collection agency.

Using these three specifications, we created a new specification called SendToCollection which will be satisfied when an invoice is overdue, when notices have been sent to the customer, and are not already with the collection agency.


OverDueSpecification OverDue = new OverDueSpecification(); NoticeSentSpecification NoticeSent = new NoticeSentSpecification(); InCollectionSpecification InCollection = new InCollectionSpecification();

ISpecification SendToCollection = OverDue.And(NoticeSent).And(InCollection.Not());

InvoiceCollection = Service.GetInvoices();

foreach (Invoice currentInvoice in InvoiceCollection) { if (SendToCollection.IsSatisfiedBy(currentInvoice)) { currentInvoice.SendToCollection();

}

}


Simple Policy

This is a simple policy based design sample. They are File Name Policy classes that I use the policy based analysis to design policies. In policy based design, the most important thing is analysis. At first, we must define the policy combined roles and implement them in the policy host classes. Then we must decompose the classes to small policy classes.

C# does not support multiple inheritance, but the multiple inheritance is the key technique in policy based design, so we must use interface to solve it.

 

Обзор

The central idiom in policy-based design is a class template (called the host class), taking several type parameters as input, which are instantiated with types selected by the user (called policy classes), each implementing a particular implicit interface (called a policy), and encapsulating some orthogonal (or mostly orthogonal) aspect of the behavior of the instantiated host class. By supplying a host class combined with a set of different, canned implementations for each policy, a library or module can support an exponential number of different behavior combinations, resolved at compile time, and selected by mixing and matching the different supplied policy classes in the instantiation of the host class template. Additionally, by writing a custom implementation of a given policy, a policy-based library can be used in situations requiring behaviors unforeseen by the library implementor. Even in cases where no more than one implementation of each policy will ever be used, decomposing a class into policies can aid the design process, by increasing modularity and highlighting exactly where orthogonal design decisions have been made.

While assembling software components out of interchangeable modules, communicating with each other through generic interfaces, is far from a new concept, policy-based design represents an innovation in the way it applies that concept at the (relatively low) level of defining the behavior of an individual class.

Policy classes have some similarity to callbacks, but differ in that, rather than consisting of a single function, a policy class will typically contain several related functions (methods), often combined with state variables and/or other facilities such as nested types.

A policy-based host class can be thought of as a type of metafunction, taking a set of behaviors represented by types as input, and returning as output a type representing the result of combining those behaviors into a functioning whole. (Unlike MPL metafunctions, however, the output is usually represented by the instantiated host class itself, rather than a nested output type.)

A key feature of the policy idiom is that, usually (though it is not strictly necessary), the host class will derive from (make itself a child class of) each of its policy classes using (public) multiple inheritance. (Alternatives are for the host class to merely contain a member variable of each policy class type, or else to inherit the policy classes privately; however inheriting the policy classes publicly has the major advantage that a policy class can add new methods, inherited by the instantiated host class and accessible to its users, which the host class itself need not even know about.) A notable feature of this aspect of the policy idiom is that, relative to object-oriented programming, policies invert the relationship between base class and derived class - whereas in OOP interfaces are traditionally represented by (abstract) base classes and implementations of interfaces by derived classes, in policy-based design the derived (host) class represents the interfaces and the base (policy) classes implement them. It should also be noted that in the case of policies, the public inheritance does not represent an is-a relationship between the host and the policy classes. While this would traditionally be considered evidence of a design defect in OOP contexts, this doesn't apply in the context of the policy idiom.

A disadvantage of policies in their current incarnation is that the policy interface doesn't have a direct, explicit representation in code, but rather is defined implicitly, via duck typing, and must be documented separately and manually, in comments.

The main idea is to use commonality-variability analysis to divide the type into the fixed implementation and interface, the policy-based class, and the different policies. The trick is to know what goes into the main class, and what policies should one create. Andrei's excellent article, mentioned above, gives us the clue: wherever we would need to make a possible limiting design decision, we should postpone that decision, we should delegate it to an appropriately named policy.

Policy classes can contain implementation, type definitions and so forth. Basically, the designer of the main template class will define what the policy classes should provide, what customization points they need to implement.

As we go by the analysis in policy-based design, it is a delicate task to create a good set of policies, just the right number. As little as necessary, but not less. The different customization points, which belong together, should go into one policy argument, such as storage policy, validation policy and so forth. A good rule of thumb during design is that you should be able to give a name to your policy, which represents a concept, and not one which represent an operation or some really tiny implementation detail. Persistence policy seems to be a good choice, while how to save policy does not.

As you do your policy-based design you will see how many other techniques will be useful, even if changed a bit, during your work. One example is that the template method pattern can be reinterpreted for compile time; so that your main class has a skeleton algorithm, which — at customization points — calls the appropriate functions of some of the policies. You will also find yourself in using your policy classes as traits are used, asking type information, delegating type related tasks to it, a storage policy is one example where it can happen.

 

Простыми словами

I love to eat food, so I thought about the policy recipe of cuisine. Maybe I can define a role to make a cuisine as follows:

Cuisine = Food + Flavor + Cooking

- The cuisine is the host combinable role. Food, Flavor and Cooking are policy groups.

- Food: Beef, Pork, Chicken, Spaghetti...

- Flavor: Acid, Sweet, Hot, Curry...

- Cooking: Roast, Cook, Fry, Stew...

- Cuisine(Steak) = Food(Beef) + Flavor(None) + Cooking(Roast)

- Cuisine(Roast chicken) = Food(Chicken) + Flavor(Curry) + Cooking(Roast) The recipe of cuisine is a combination of food, flavor and cooking.

 

 

A different policy can combine a different cuisine. I think this explanation is easiest to understand.


The base interface is CBINamePolicy, it just has a property Name.

All of FileNamePolicy classes will implement these interfaces including CBIFileNamePolicy, CBIFilenameExtensionPolicy and CBIFileAnmeControl.




 

Пример реализации

These classes implement CBIFileNameControlPolicy and CBILargeFileSplitPolicy interface.

 

/// <summary>

/// Name property interface policy /// </summary>

public interface CBINamePolicy {

/// <summary> /// Name Property /// </summary> string Name {

get; set;

}

}

 

/// <summary>

/// The base policy interface for file name /// </summary>

public interface CBIFileNamePolicy: CBINamePolicy

{

}

 

/// <summary>

/// Basic implement for CBIFileNamePolicy /// </summary>

public class CBFileNamePolicy: CBIFileNamePolicy

{

/// <summary>


/// File Name /// </summary> public string Name {

get; set;

}

}

/// <summary>

/// Get full path file name. /// EX: C:/Test/MyFileName /// </summary>

public class CBFullFileNamePolicy: CBIFileNamePolicy

{

/// <summary> /// File Name /// </summary> string m_name;

/// <summary>

/// Setter: set value to name

/// Getter: get full path with name

/// </summary>

public string Name

{

get {

return CBGeneral.GetFullPath(m_name);

}

set

{

m_name = value;

}

}

}

/// <summary>

/// Readonly policy, to get yyyyMMdd file name /// EX: 20110923 /// </summary>

public class CBDateNowNamePolicy: CBIFileNamePolicy

{

#region CBINamePolicy Members

public string Name {

get

return DateTime.Now.ToString("yyyyMMdd");

set

throw new NotSupportedException ("CBDateNowNamePolicy.Name is a readonly property.");

 

}

#endregion

}

/// <summary>

/// File name with date yyyyMMdd

/// EX: MyFileName20110923


/// </summary>

public class CBPreDateNowNamePolicy: CBIFileNamePolicy

{

string m_name;

#region CBINamePolicy Members

public string Name {

get {

return m_name + DateTime.Now.ToString("yyyyMMdd");

}

set

{

m_name = value;

}

}

#endregion

}

/// <summary>

/// Full path with date yyyyMMdd /// EX: C:/Test/20110923

/// </summary>

public class CBFullDateNowNamePolicy: CBIFileNamePolicy

{

#region CBINamePolicy Members

public string Name {

get

return CBGeneral.GetFullPath(DateTime.Now.ToString("yyyyMMdd"));

set

throw new NotSupportedException ("CBFullPathDateNowNamePolicy.Name is a readonly property.");

 

}

#endregion

}

/// <summary>

/// Full file name with date yyyyMMdd /// EX: C:/Test/MyFileName20110923

/// </summary>

public class CBFullPreDateNowNamePolicy: CBIFileNamePolicy

{

string m_name;

#region CBINamePolicy Members

public string Name {

get {

return CBGeneral.GetFullPath(m_name + DateTime.Now.ToString("yyyyMMdd"));

}

set

{


m_name = value;

 

 

#endregion

 

 

// In these classes, the Name property will get a string for file name.

//

/// <summary>

/// The base policy interface for file extension name. /// </summary>

public interface CBIFilenameExtensionPolicy: CBIFileNamePolicy {

}

/// <summary>

/// txt extension policy

/// </summary>

public class CBTextExtensionPolicy: CBIFilenameExtensionPolicy

{

#region CBIFilenameExtensionPolicy Members

public string Name {

{

return "txt";

}

set

{

throw new NotImplementedException();

}

}

#endregion

}

/// <summary>

/// xml extension policy

/// </summary>

public class CBXmlExtensionPolicy: CBIFilenameExtensionPolicy

{

#region CBIFilenameExtensionPolicy Members

public string Name {

get {

return "xml";

}

set

{

throw new NotImplementedException();

}

}

#endregion

}

/// <summary>

/// log extension policy /// </summary>

public class CBLogExtensionPolicy: CBIFilenameExtensionPolicy

{

#region CBIFilenameExtensionPolicy Members

public string Name {

get {

return "log";

}

set

{

throw new NotImplementedException();

}

}

#endregion

}

/// <summary>

/// int extension policy

/// </summary>

public class CBIniExtensionPolicy: CBIFilenameExtensionPolicy

{

#region CBIFilenameExtensionPolicy Members

public string Name {

get {

return "ini";

}

set

{

throw new NotImplementedException();

}

}

#endregion

}

 

// These are readonly classes, just return extension name. //

/// <summary>

/// The base policy interface for file name control. /// </summary>

public interface CBIFileNameControlPolicy: CBIFileNamePolicy {

void Control();

}

/// <summary>

/// File split size

/// </summary>

public interface CBIFileSplitSizePolicy

{

long MaxSplitSize {

get; set;


/// <summary>

/// Use for large file split /// </summary>

public interface CBILargeFileSplitPolicy

{

int Index {

get;

set;

}

}

/// <summary> /// File split policy

/// If file exist and file size large more than setting it will split file. /// EX: 01, 02 or 03 /// </summary>

public class CBFileNumSplitPolicy: CBILargeFileSplitPolicy, CBIFileNameControlPolicy

{

public CBFileNumSplitPolicy() {

Index = 1;

}

public int Index {

get;

set;

}

public virtual string Name {

get {

return Index.ToString("00");

}

set

{

throw new NotImplementedException();

}

}

public void Control() {

Index++;

}

}

/// <summary>

/// File split policy

/// If file exist and file size large more than setting it will split file. /// EX: AA, AB or AC /// </summary>

public class CBFileTextSplitPolicy: CBFileNumSplitPolicy

{

public CBFileTextSplitPolicy(): base()

{

Index = 0;

}

public override string Name

get

{

int chl = (Index / 26) + 0x41; int ch2 = (index % 26) + 0x41;

return string.Format("{0}{1}", Convert.ToChar(ch1), Convert.ToChar(ch2));

}

set

{

throw new NotImplementedException();

}

}

}

//

// These are file names split with split size property.

// Sometimes the log file will split with file size, so I use this policy to control file name.

//

/// <summary>

/// Abstract base class for file name policy host.

/// How to use:

/// CBFileName filename = new CBFileName<CBIFileNamePolicy>();

/// CBFileName filename = new CBFileName<CBIFileNamePolicy,CBIFilenameExtensionPolicy>();

/// CBFileName filename = new CBFileName<CBIFileNamePolicy,

/// CBIFileNameControlPolicy,CBIFilenameExtensionPolicy>();

/// Just call fliename.Name to get file name

/// filename.MaxSplitSize will use for CBIFileNameControlPolicy

/// </summary>

public abstract class CBFileName: CBIFileNamePolicy, CBIFileSplitSizePolicy

{

const long DEFAULT_SPLIT_SIZE = 1024 * 1024 * 5; //const long DEFAULT_SPLIT_SIZE = 100; public virtual string Name

get; set;

public long MaxSplitSize

get; set;

 

public CBFileName()

MaxSplitSize = DEFAULT_SPLIT_SIZE;

 

}

 

 

/// <summary>

/// This policy will get name from CBIFileNamePolicy /// </summary>

/// <typeparam name="TNamePolicy">Must be CBIFileNamePolicy</typeparam> public sealed class CBFileName<TNamePolicy>: CBFileName where TNamePolicy: CBIFileNamePolicy, new()

TNamePolicy _policy; public CBFileName(): base()


_policy = new TNamePolicy();

public override string Name

{

get

{

return _policy.Name;

}

set

{

_policy.Name = value;

}

}

}

 

// This is first policy host class. // It accesses _policy.Name only.

//

/// <summary>

/// This policy will get name from CBIFileNamePolicy.Name + "." + /// CBIFilenameExtensionPolicy.Name /// </summary>

/// <typeparam name="TNamePolicy">Must be CBIFileNamePolicy</typeparam> /// <typeparam name="TNameExtensionPolicy">Must be CBIFilenameExtensionPolicy</ty public sealed class CBFileName<TNamePolicy, TNameExtensionPolicy>: CBFileName where TNamePolicy: CBIFileNamePolicy, new() where TNameExtensionPolicy: CBIFilenameExtensionPolicy, new()

{

TNamePolicy _policy1; TNameExtensionPolicy _policy2; public CBFileName(): base()

{

_policy1 = new TNamePolicy(); _policy2 = new TNameExtensionPolicy();

}

public override string Name {

get {

return _policy1.Name + "." + _policy2.Name;

}

set

{

_policy1.Name = value;

}

}

}

 

 

This policy host class has two generic arguments.

In this role, CBFileName.Name will return _policy1.Name + "." _policy2.Name. The _policy1 is CBIFileNamePolicy, _policy2 is CBIFilenameExtensionPolicy.

 

 

CBFileName namel =

new CBFileName<CBFileNamePolicy, CBTextExtensionPolicy>(); namel.Name = "Testl";

Console.WriteLine(namel.Name);

 

We can use it like this sample.

The Name property will get Test1.txt.

/// <summary>

/// This policy will get name from

/// CBIFileNamePolicy.Name + "_" + CBIFileNameControlPolicy.Name + /// "." + CBIFilenameExtensionPolicy.Name /// </summary>

/// <typeparam name="TNamePolicy">Must be CBIFileNamePolicy</typeparam>

/// <typeparam name="TNameControlPolicy">Must be CBIFileNameControlPolicy</typeparam>

/// <typeparam name="TNameExtensionPolicy">

/// Must be CBIFilenameExtensionPolicy</typeparam>

public sealed class CBFileName<TNamePolicy, TNameControlPolicy,

TNameExtensionPolicy>: CBFileName, CBIFileNameControlPolicy

where TNamePolicy: CBIFileNamePolicy, new()

where TNameControlPolicy: CBIFileNameControlPolicy, new()

where TNameExtensionPolicy: CBIFilenameExtensionPolicy, new()

{

TNamePolicy _policy1; TNameControlPolicy _policy2; TNameExtensionPolicy _policy3;

public CBFileName(): base()

{

_policy1 = new TNamePolicy(); _policy2 = new TNameControlPolicy(); _policy3 = new TNameExtensionPolicy();

}

string FileName {

get {


Дата добавления: 2015-09-29; просмотров: 33 | Нарушение авторских прав







mybiblioteka.su - 2015-2024 год. (0.096 сек.)







<== предыдущая лекция | следующая лекция ==>