From the official
The policy pattern defines a series of algorithms, encapsulates each algorithm, and allows them to be replaced with each other. The policy pattern makes the algorithm independent of the customers who use it.
Structure diagram
Simple implementations in Ruby (code from Ruby design mode)
# According to the duck model, there is no policy object to inherit a base class that provides a uniform interface
#策略1
Class HTMLFormatter
Def output_report title, text
Puts ‘<html>‘
Puts ‘ <head> ‘
Puts ‘ <title>‘ + title + ‘</title>‘
Puts ‘ </head> ‘
Puts ‘ <body>‘
Text.each do |line|
Puts "<p>#{line}</p>"
End
Puts ‘ </body> ‘
Puts ‘</html>‘
End
End
#策略2
Class PlainTextFormatter
Def output_report title, text
Puts ‘******** ‘ + title + ‘ ********’
Text.each do |line|
Puts line
End
End
End
# The reason why this strategy is used is because Reporter may print text into text in different formats, if needed in the future
#Support xml then only need to add a class that supports the output_report interface.
Class Reporter
Attr_reader :title, :text
Attr_accessor :formater
Def initialize formater
@title = ‘My Report’
@text = [‘This is my report‘, ‘Please see the report’, ‘It is ok’]
@formater = formater
End
# Policy objects have a common interface
Def output_report
@formater.output_report @title, @text
End
End
# Environment object calls different strategies
Reporter.new(HTMLFormatter.new).output_report
Reporter.new(PlainTextFormatter.new).output_report
So when can the strategy model be used?
Let's start with an example, in general, if we want to do data validation, many times are based on the Swith statement to judge, but this brings a few problems, first of all, if we increase the demand, we also need to modify this code to add logic, and in the unit test is more complex, The code is as follows:
# At the earliest time, I almost used this method to do the data validation of the form.
Validator = {
Validate: function (value, type) {
Switch (type) {
Case ‘isNonEmpty ‘:
{
Return true; // NonEmpty verification result
}
Case ‘isNumber ‘:
{
Return true; // Number verification result
Break;
}
Case ‘isAlphaNum ‘:
{
Return true; // AlphaNum verification result
}
Default:
{
Return true;
}
}
}
};
// test
Alert(validator.validate("123", "isNonEmpty"));
OK, with the strategy model to change, first analysis of validator is a strategic environment, different authentication is a policy object, we need to do is to allow different policy objects to support the unified interface
The refactoring code is as follows:
Var validator = {
Types: {},
Messages: [],
Config: {},
Validate: function (data) {
Var i, msg, type, checker, result_ok;
This.messages = [];
For (i in data) {
If (data.hasOwnProperty(i)) {
Type = this.config[i]; // Query whether there is an existing validation rule based on the key
Checker = this.types[type]; // Get the validation class for the validation rule
If (!type) {
Continue; // If the validation rule does not exist, it will not be processed
}
If (!checker) { // throw an exception if the validation rule class does not exist
Throw {
Name: "ValidationError",
Message: "No handler to validate type " + type
};
}
# Start performing different strategies on demand
Result_ok = checker.validate(data[i]); // Verify using the single validation class found
If (!result_ok) {
Msg = "Invalid value for *" + i + "*, " + checker.instructions;
This.messages.push(msg);
}
}
}
Return this.hasErrors();
},
hasErrors: function () {
Return this.messages.length !== 0;
}
};
// Verify that the given value is not empty
validator.types.isNonEmpty = {
Validate: function (value) {
Return value !== "";
},
Instructions: "The value passed in cannot be empty"
};
// Verify that the given value is a number
Validator.types.isNumber = {
Validate: function (value) {
Return !isNaN(value);
},
Instructions: "The value passed in can only be a legal number, for example: 1, 3.14 or 2010"
};
// Verify that the given value is just a letter or number
Validator.types.isAlphaNum = {
Validate: function (value) {
Return !/[^a-z0-9]/i.test(value);
},
Instructions: "The value passed in can only protect letters and numbers, not special characters"
};
Var data = {
First_name: "Tom",
Last_name: "Xu",
Age: "unknown",
Username: "TomXu"
};
Validator.config = {
First_name: ‘isNonEmpty‘,
Age: ‘isNumber‘,
Username: ‘isAlphaNum‘
};
Validator.validate(data);
If (validator.hasErrors()) {
Console.log(validator.messages.join("\n"));
}
Early time to see this code I just want to say fuck, obviously very simple things, not to engage in such a complex is necessary?
Of course, assuming the project is simple, use the code before refactoring OK, and here's just an example.
So how does the same need to be achieved with Ruby?
Here's the code: (I'm writing Ruby with JavaScript in mind, I don't know if there's a problem with reading)
# coding: utf-8
# Strategy 1
Class IsNonEmpty
Def self.check(data)
Data.nil?
End
Def self.notice
"The value passed in cannot be empty \n"
End
End
# Strategy 2
Class IsAlphaNum
Def self.check(data)
Data =~ /[^a-z0-9]/
End
Def self.notice
"Incoming values can only protect letters and numbers, cannot contain special characters \n"
End
End
# surroundings
Class Validate
Attr_reader :msg
Def initialize
# @types = []
@msg = ‘‘
# @config = {}
End
# This place is not written according to the logic of javascript, if you need different instances to support different verification,
# Can add policy objects to @types
@config Same as
# def add_types(type)
# @types << type
# end
Def check(hash_data)
Hash_data.each_pair do |key, val|
@msg << "#{key}errors #{val[:type].notice}" if val[:type].check(val[:data])
End
End
Def errors
!@msg.nil?
End
End
Validate = Validate.new
Hash_data = {
First: {type: IsNonEmpty, data: 1324},
Second: {type: IsNonEmpty, data: nil},
Third: {type: IsAlphaNum, data: ‘123angel‘},
Forth: {type: IsAlphaNum, data: ‘123sf@/!’}
}
Validate.check(hash_data)
If validate.errors
Puts validate.msg
End
Finally: This is intended to summarize the differences between Ruby and JavaScript shared interface, but the ability is limited, can only be understood and can not be expressed
Or maybe it's for the lack of understanding of the key points.
Strategy patterns for Ruby and JavaScript