One, foreword
I was reading a framework document the other day that said, “From the object factory… , prompting the writing of this article. Although some patterns are simple and simple, they are common and useful.
Review some of the patterns applied in the context of a recent project scenario <Singleton, Factory, Strategy>.
- Singleton: Creation pattern, responsible for creating and maintaining a globally unique instance
- Factory: The creation pattern, the object Factory is responsible for creating or getting specific instance objects based on the identity
- Strategy: Behavioral/runtime pattern, where policies are responsible for controlling the behavior of the application runtime based on identity
Second, scene context
Project requirements/scenarios: Use scripts to “automate testing” specific applications by adding accessibility tools, such as clicking buttons, selecting menus, reading control content, etc.
Original implementation: The script <AutoIt> performs “automated tests” on a particular application by calculating coordinates. Disadvantages: script workload, rely on button screen coordinates, coordinate calculation complex, rely on screen resolution, etc.
Target program simplification diagram:
Before using assistive tools:
After using assistive tools:
Analysis and design
Analysis and design of auxiliary tools are only carried out here, others are skipped.
-
FIG. 1 The target program has the following main characteristics:
- The target program is divided into five functional areas A-E
- Each functional area has buttons, menus, and similar functions
- Each functional area has its own unique function
-
Auxiliary tools provide unified calls externally
-
Assistive tools can be invoked repeatedly, but do not support concurrent operations
Based on the above analysis:
- The Operator < manipulation code or specific manipulation behavior > is divided into five specific operators: AOperator, BOperator, COperator, DOperator and EOperator, which correspond to different application areas of operation respectively.
- Use the create mode to manage operators
- Use locks to limit concurrency
- The outer layer encapsulates a singleton
Fourth, the UML
Five, the Code of the Show
1. AuxiliaryToolSingleton provides calls externally and controls concurrency with a locking mechanism.
using System;
using System.Threading;
using DesignPatternDemo.Operator;
namespace DesignPatternDemo
{
public class AuxiliaryToolSingleton
{
public static Semaphore OperatorSemaphore = new Semaphore(1, 1);
private static readonly object OperatorLock = new object();
public static AuxiliaryToolSingleton Instance = new AuxiliaryToolSingleton();
private AuxiliaryToolSingleton()
{
RegistorOperator(OperatorFactory.Instance);
}
public void CallOperator(string operatorName, params string[] operatorParams)
{
//OperatorSemaphore.WaitOne();
lock (OperatorLock)
{
Console.WriteLine($"Call method CallOperator :{operatorName} .Current Thread:{Thread.CurrentThread.ManagedThreadId}");
BaseOperator concreteOperator = OperatorFactory.Instance.GetOperator(operatorName);
concreteOperator.InitializationParameters(operatorParams);
concreteOperator.Execute();
}
//OperatorSemaphore.Release();
}
public static void RegistorOperator(OperatorFactory factory)
{
factory.Register(nameof(AOperator), new AOperator());
factory.Register(nameof(BOperator), new BOperator());
factory.Register(nameof(COperator), new COperator());
factory.Register(nameof(DOperator), new DOperator());
factory.Register(nameof(EOperator), new EOperator());
}
}
}
AuxiliaryToolSingleton
Copy the code
2. BaseOperator controls the base class, which contains information about public methods, virtual methods, and parameters.
using System; using System.Threading; namespace DesignPatternDemo.Operator { public class BaseOperator { public string Name { get; set; } public string Description { get; set; } public void Execute() { //ToDo Thread.Sleep(new Random().Next(0, 5) * 1000); Console.WriteLine($"Execute concrete operator:{GetType().Name} .Current Thread:{Thread.CurrentThread.ManagedThreadId}"); ConcreteOperate($"{GetType().Name}"); } public void InitializationParameters(params string[] operatorParams) { //ToDo Console.WriteLine($"Initialization Parameters :{GetType().Name}"); } private void ConcreteOperate(string mark) { // ToDo Console.WriteLine($"The concrete operation :{mark} was performed successfully .\r\n"); } public virtual void ClickButtonByMark(string mark) { // ToDo ConcreteOperate(mark); } public virtual void ClickPopupMenuByMark(string mark) { // ToDo ConcreteOperate(mark); } public virtual void SelectDropdownBoxByIndex(int dropBoxIndex) { // ToDo ConcreteOperate($"{dropBoxIndex}"); } } } BaseOperatorCopy the code
3. AOperator concrete manipulation class < such as button click >, implement ISpecialOperateA, inherit BaseOperator.
using System;
namespace DesignPatternDemo.Operator
{
public class AOperator : BaseOperator, ISpecialOperateA
{
public void SetContent(string content)
{
//ToDo
Console.WriteLine($"Filled the content:{content} successfully");
}
public string GetContent()
{
//ToDo
return $"{new Random().Next()}{Guid.NewGuid()}";
}
}
}
namespace DesignPatternDemo.Operator
{
public interface ISpecialOperateA
{
void SetContent(string content);
string GetContent();
}
}
AOperator
Copy the code
4. BOperator, COperator, and DOperator specific manipulation classes.
namespace DesignPatternDemo.Operator
{
public class BOperator : BaseOperator
{
}
}
namespace DesignPatternDemo.Operator
{
public class COperator : BaseOperator
{
}
}
namespace DesignPatternDemo.Operator
{
public class DOperator : BaseOperator
{
}
}
Concrete Operator
Copy the code
5. EOperator specific manipulation class < such as manipulation tree control >, ISpecialOperateE, inherit BaseOperator.
using System;
namespace DesignPatternDemo.Operator
{
public class EOperator : BaseOperator, ISpecialOperateE
{
public void ClickTreeviewByMark(string mark)
{
//ToDo
Console.WriteLine($"{mark}: execution succeed");
}
}
}
namespace DesignPatternDemo.Operator
{
public interface ISpecialOperateE
{
void ClickTreeviewByMark(string mark);
}
}
EOperator
Copy the code
6. Factory Base class, you can register, delete, and obtain specific classes according to the key. One of the creation patterns.
using System.Collections.Generic;
namespace DesignPatternDemo
{
public class Factory<TF, TV> where TF : new()
{
protected Factory()
{
KeyValues = new Dictionary<string, TV>();
}
public static TF Instance { get; set; } = new TF();
private Dictionary<string, TV> KeyValues { get; }
public TV GetItem(string key)
{
KeyValues.TryGetValue(key, out TV find);
return find;
}
public void Register(string key, TV t)
{
UnRegister(key);
KeyValues.Add(key, t);
}
public void UnRegister(string key)
{
if (KeyValues.ContainsKey(key)) KeyValues.Remove(key);
}
}
}
Factory
Copy the code
7. OperatorFactory Specifies the Factory.
using DesignPatternDemo.Operator;
namespace DesignPatternDemo
{
public class OperatorFactory : Factory<OperatorFactory, BaseOperator>
{
public BaseOperator GetOperator(string operatorName)
{
return GetItem(operatorName);
}
}
}
OperatorFactory
Copy the code
8. Program console Program, using parallel library and Task multi-threaded call simulation respectively.
using System; using System.Collections.Generic; using System.Threading.Tasks; using DesignPatternDemo.Operator; namespace DesignPatternDemo { internal class Program { private static void Main(string[] args) { Console.WriteLine("Hello World!" ); List<string> concreteOperators = GetConcreteOperators(); Parallel.ForEach(concreteOperators, current => { CallOperator(current); }); foreach (string operatorName in concreteOperators) { Task concreteTask = new Task(() => { CallOperator(operatorName); }); concreteTask.Start(); } Console.ReadKey(); } private static List<string> GetConcreteOperators() { List<string> concreteOperators = new List<string> { nameof(AOperator), nameof(BOperator), nameof(COperator), nameof(DOperator), nameof(EOperator) }; return concreteOperators; } private static void CallOperator(string operatorName, params string[] operatorParams) { AuxiliaryToolSingleton auxiliaryTool = AuxiliaryToolSingleton.Instance; auxiliaryTool.CallOperator(operatorName, operatorParams); } } } ProgramCopy the code
Six, explanation, summary
- The purpose of this article is to review the use of some patterns, the business of the original project, the code structure, and the implementation language, all of which have been changed or simplified.
- As described in UML, it can be implemented in any OO language.
- If there are many conditions, you can use “table driven method” and Strategy pattern avoidance.
- Pattern routines and their corresponding scenarios.
- Demo code environment: VS2017.net Core2.2