Шаблоны проектирования помогают следовать передовым методам программирования. Spring framework, один из самых популярных веб-фреймворков, также использует некоторые из них.
В этой статье будут представлены шаблоны проектирования, используемые в Spring Framework. Это первая часть из 5 тематических статей. На этот раз мы познакомимся с 4 шаблонами проектирования, используемыми в среде Spring: интерпретатор, построитель, фабричный метод и абстрактная фабрика. В каждом разделе сначала будет объяснено обоснование данного режима. Далее для углубления понимания будет использован пример Spring.
Шаблоны проектирования интерпретатора
В реальном мире мы, люди, должны интерпретировать жесты. Они могут иметь разное значение для культуры. Вот наша интерпретация, придающая им значение. В программировании нам также нужно проанализировать вещь и решить, что она означает. мы можем использоватьОбъясните шаблоны проектированиясделать это.
Этот шаблон основан на частях выражения и вычислителя. Первый представляет собой предмет для анализа. Этот анализ производится оценщиками, знающими значение символов, составляющих выражение. Ненужные операции выполняются внутри контекста.
Весна в основном основана наSpring Expression Language(Заклинание) например. Небольшое напоминание о том, что SpEL — этоorg.springframework.expression.ExpressionParserЯзык, реализующий анализ и выполнение. Эти реализации принимают выражения Spel, заданные в виде строк, и преобразуют их вorg.springframework.expression.Expressionпример. Контекстный компонентorg.springframework.expression.EvaluationContextПредставление реализации, например: StandardEvaluationContext.
Вот пример из Spel:
Writer writer = new Writer();
writer.setName("Writer's name");
StandardEvaluationContext modifierContext = new StandardEvaluationContext(subscriberContext);
modifierContext.setVariable("name", "Overriden writer's name");
parser.parseExpression("name = #name").getValue(modifierContext);
System.out.println("writer's name is : " + writer.getName());
Вывод должен вывести «Имя переопределенного автора». Как видите, свойства объекта определяются выражениемname = #name
изменено, это выражение толькоExpressionParser
можно понять, потому что он обеспечиваетcontext
(в предыдущем примереmodifierContext
пример).
режим строителя
Шаблон проектирования строителяЭто первый режим предметного режима «Три мушкетера создания». Этот шаблон используется для упрощения построения сложных объектов. Чтобы понять эту концепцию, представьте себе объект с резюме программиста. В этом объекте мы хотим хранить личную информацию (имя, адрес и т. д.), а также техническую информацию (знание языка, реализованные проекты и т. д.). Конструкция этого объекта может выглядеть так:
// with constructor
Programmer programmer = new Programmer("first name", "last name", "address Street 39", "ZIP code", "City", "Country", birthDateObject, new String[] {"Java", "PHP", "Perl", "SQL"}, new String[] {"CRM system", "CMS system for government"});
// or with setters
Programmer programmer = new Programmer();
programmer.setName("first name");
programmer.setLastName("last name");
// ... multiple lines after
programmer.setProjects(new String[] {"CRM system", "CMS system for government"});
Builder позволяет нам четко декомпозировать конструкцию объекта, используя внутренние объекты Builder, которые передают значения в родительский класс. Таким образом, для создания нашего объекта резюме программиста построитель может выглядеть так:
public class BuilderTest {
@Test
public void test() {
Programmer programmer = new Programmer.ProgrammerBuilder().setFirstName("F").setLastName("L")
.setCity("City").setZipCode("0000A").setAddress("Street 39")
.setLanguages(new String[] {"bash", "Perl"}).setProjects(new String[] {"Linux kernel"}).build();
assertTrue("Programmer should be 'F L' but was '"+ programmer+"'", programmer.toString().equals("F L"));
}
}
class Programmer {
private String firstName;
private String lastName;
private String address;
private String zipCode;
private String city;
private String[] languages;
private String[] projects;
private Programmer(String fName, String lName, String addr, String zip, String city, String[] langs, String[] projects) {
this.firstName = fName;
this.lastName = lName;
this.address = addr;
this.zipCode = zip;
this.city = city;
this.languages = langs;
this.projects = projects;
}
public static class ProgrammerBuilder {
private String firstName;
private String lastName;
private String address;
private String zipCode;
private String city;
private String[] languages;
private String[] projects;
public ProgrammerBuilder setFirstName(String firstName) {
this.firstName = firstName;
return this;
}
public ProgrammerBuilder setLastName(String lastName) {
this.lastName = lastName;
return this;
}
public ProgrammerBuilder setAddress(String address) {
this.address = address;
return this;
}
public ProgrammerBuilder setZipCode(String zipCode) {
this.zipCode = zipCode;
return this;
}
public ProgrammerBuilder setCity(String city) {
this.city = city;
return this;
}
public ProgrammerBuilder setLanguages(String[] languages) {
this.languages = languages;
return this;
}
public ProgrammerBuilder setProjects(String[] projects) {
this.projects = projects;
return this;
}
public Programmer build() {
return new Programmer(firstName, lastName, address, zipCode, city, languages, projects);
}
}
@Override
public String toString() {
return this.firstName + " "+this.lastName;
}
}
Как видите, сложность построения объектов скрыта за конструктором, а внутренний статический класс принимает вызовы цепочек методов. Весной мы можемorg.springframework.beans.factory.support.BeanDefinitionBuilderкласс для получения этой логики. Это класс, который позволяет нам программно определять bean-компоненты. мы можем поговорить опостпроцессор bean factoryсмотрите в статье,BeanDefinitionBuilder
содержит несколько методов, которыеAbstractBeanDefinitionСоответствующие реализации абстрактных классов задают значения, такие как области действия, фабричные методы, свойства и т. д. Чтобы увидеть, как это работает, проверьте эти методы:
public class BeanDefinitionBuilder {
/**
* The {@code BeanDefinition} instance we are creating.
*/
private AbstractBeanDefinition beanDefinition;
// ... some not important methods for this article
// Some of building methods
/**
* Set the name of the parent definition of this bean definition.
*/
public BeanDefinitionBuilder setParentName(String parentName) {
this.beanDefinition.setParentName(parentName);
return this;
}
/**
* Set the name of the factory method to use for this definition.
*/
public BeanDefinitionBuilder setFactoryMethod(String factoryMethod) {
this.beanDefinition.setFactoryMethodName(factoryMethod);
return this;
}
/**
* Add an indexed constructor arg value. The current index is tracked internally
* and all additions are at the present point.
* @deprecated since Spring 2.5, in favor of {@link #addConstructorArgValue}
*/
@Deprecated
public BeanDefinitionBuilder addConstructorArg(Object value) {
return addConstructorArgValue(value);
}
/**
* Add an indexed constructor arg value. The current index is tracked internally
* and all additions are at the present point.
*/
public BeanDefinitionBuilder addConstructorArgValue(Object value) {
this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(
this.constructorArgIndex++, value);
return this;
}
/**
* Add a reference to a named bean as a constructor arg.
* @see #addConstructorArgValue(Object)
*/
public BeanDefinitionBuilder addConstructorArgReference(String beanName) {
this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(
this.constructorArgIndex++, new RuntimeBeanReference(beanName));
return this;
}
/**
* Add the supplied property value under the given name.
*/
public BeanDefinitionBuilder addPropertyValue(String name, Object value) {
this.beanDefinition.getPropertyValues().add(name, value);
return this;
}
/**
* Add a reference to the specified bean name under the property specified.
* @param name the name of the property to add the reference to
* @param beanName the name of the bean being referenced
*/
public BeanDefinitionBuilder addPropertyReference(String name, String beanName) {
this.beanDefinition.getPropertyValues().add(name, new RuntimeBeanReference(beanName));
return this;
}
/**
* Set the init method for this definition.
*/
public BeanDefinitionBuilder setInitMethodName(String methodName) {
this.beanDefinition.setInitMethodName(methodName);
return this;
}
// Methods that can be used to construct BeanDefinition
/**
* Return the current BeanDefinition object in its raw (unvalidated) form.
* @see #getBeanDefinition()
*/
public AbstractBeanDefinition getRawBeanDefinition() {
return this.beanDefinition;
}
/**
* Validate and return the created BeanDefinition object.
*/
public AbstractBeanDefinition getBeanDefinition() {
this.beanDefinition.validate();
return this.beanDefinition;
}
}
заводской метод
Второй член Трех мушкетеров, создавший шаблон объекта,Шаблон проектирования фабричного метода. Он идеально подходит для использования динамической среды в качестве среды Spring. На самом деле этот шаблон позволяет инициализировать объекты с помощью общедоступных статических методов, известных как фабричные методы. В этой концепции нам нужно определить интерфейс для создания объектов. Но создание производится классом, который использует связанный объект.
Но прежде чем погрузиться в мир Spring, давайте сделаем пример с кодом Java:
public class FactoryMethodTest {
@Test
public void test() {
Meal fruit = Meal.valueOf("banana");
Meal vegetable = Meal.valueOf("carrot");
assertTrue("Banana should be a fruit but is "+fruit.getType(), fruit.getType().equals("fruit"));
assertTrue("Carrot should be a vegetable but is "+vegetable.getType(), vegetable.getType().equals("vegetable"));
}
}
class Meal {
private String type;
public Meal(String type) {
this.type = type;
}
public String getType() {
return this.type;
}
// Example of factory method - different object is created depending on current context
public static Meal valueOf(String ingredient) {
if (ingredient.equals("banana")) {
return new Meal("fruit");
}
return new Meal("vegetable");
}
}
В Spring мы можем создавать bean-компоненты, указав фабричные методы. Этот метод точно такой же, как и метод valueOf, показанный в предыдущих примерах кода. Он статичен и не может принимать ни одного аргумента или больше. Чтобы лучше понять дело, давайте посмотрим на пример. Сначала получите конфигурацию:
<bean id="welcomerBean" class="com.mysite.Welcomer" factory-method="createWelcomer">
<constructor-arg ref="messagesLocator"></constructor-arg>
</bean>
<bean id="messagesLocator" class="com.mysite.MessageLocator">
<property name="messages" value="messages_file.properties"></property>
</bean>
Теперь сосредоточимся на инициализации этого bean-компонента:
public class Welcomer {
private String message;
public Welcomer(String message) {
this.message = message;
}
public static Welcomer createWelcomer(MessageLocator messagesLocator) {
Calendar cal = Calendar.getInstance();
String msgKey = "welcome.pm";
if (cal.get(Calendar.AM_PM) == Calendar.AM) {
msgKey = "welcome.am";
}
return new Welcomer(messagesLocator.getMessageByKey(msgKey));
}
}
Когда Spring создаст welcomeBean, он будет проходить не через традиционный конструктор, а через определенный статический фабричный метод createWelcomer. Также обратите внимание, что этот метод принимает некоторые параметры (экземпляр бина MessageLocator, содержащий все доступные сообщения) теги, обычно зарезервированные для традиционных конструкторов.
абстрактная фабрика
последний,Абстрактная фабричная конструкция, который похож на фабричный метод. Разница в том, что мы можем думать об абстрактной фабрике как о фабрике в индустриальном смысле этого слова, т.е. как то, что обеспечивает желаемый объект. Фабричные компоненты: абстрактная фабрика, абстрактный продукт, продукт и клиент. Точнее, абстрактная фабрика определяет методы построения объектов. Абстрактный продукт является результатом этой структуры. Продукт – это конкретный результат с одинаковой структурой. Заказчик — это человек, который просит создать продукт, чтобы абстрагироваться от фабрики.
Опять же, прежде чем углубляться в детали Spring, мы сначала проиллюстрируем концепцию на примере кода Java:
public class FactoryTest {
// Test method which is the client
@Test
public void test() {
Kitchen factory = new KitchenFactory();
KitchenMeal meal = factory.getMeal("P.1");
KitchenMeal dessert = factory.getDessert("I.1");
assertTrue("Meal's name should be 'protein meal' and was '"+meal.getName()+"'", meal.getName().equals("protein meal"));
assertTrue("Dessert's name should be 'ice-cream' and was '"+dessert.getName()+"'", dessert.getName().equals("ice-cream"));
}
}
// abstract factory
abstract class Kitchen {
public abstract KitchenMeal getMeal(String preferency);
public abstract KitchenMeal getDessert(String preferency);
}
// concrete factory
class KitchenFactory extends Kitchen {
@Override
public KitchenMeal getMeal(String preferency) {
if (preferency.equals("F.1")) {
return new FastFoodMeal();
} else if (preferency.equals("P.1")) {
return new ProteinMeal();
}
return new VegetarianMeal();
}
@Override
public KitchenMeal getDessert(String preferency) {
if (preferency.equals("I.1")) {
return new IceCreamMeal();
}
return null;
}
}
// abstract product
abstract class KitchenMeal {
public abstract String getName();
}
// concrete products
class ProteinMeal extends KitchenMeal {
@Override
public String getName() {
return "protein meal";
}
}
class VegetarianMeal extends KitchenMeal {
@Override
public String getName() {
return "vegetarian meal";
}
}
class FastFoodMeal extends KitchenMeal {
@Override
public String getName() {
return "fast-food meal";
}
}
class IceCreamMeal extends KitchenMeal {
@Override
public String getName() {
return "ice-cream";
}
}
Как видно из этого примера, абстрактная фабрика инкапсулирует создание объектов. Создание объекта может использовать тот же шаблон фабричного метода, что и классический конструктор. В Spring примером фабрики являетсяorg.springframework.beans.factory.BeanFactory. Благодаря его реализации мы можем получить доступ к компонентам из контейнера Spring. В зависимости от принятой стратегииgetBeanМетод может возвращать созданный объект (общий экземпляр, одноэлементная область) или инициализировать новый объект (область прототипа). существуетBeanFactory
В реализации можно выделить:ClassPathXmlApplicationContext
,XmlWebApplicationContext
,StaticWebApplicationContext
,StaticPortletApplicationContext
,GenericApplicationContext
,StaticApplicationContext
.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"file:test-context.xml"})
public class TestProduct {
@Autowired
private BeanFactory factory;
@Test
public void test() {
System.out.println("Concrete factory is: "+factory.getClass());
assertTrue("Factory can't be null", factory != null);
ShoppingCart cart = (ShoppingCart) factory.getBean("shoppingCart");
assertTrue("Shopping cart object can't be null", cart != null);
System.out.println("Found shopping cart bean:"+cart.getClass());
}
}
В этом случае абстрактная фабрика представлена интерфейсом BeanFactory. Бетонный завод печатается в первом System.out иorg.springframework.beans.factory.support.DefaultListableBeanFactoryпример. Его абстрактный продукт — объект. В нашем случае конкретный продукт — это абстрактный продукт (объект), который принудительно помещается в экземпляр ShoppingCart.
Первая статья познакомила нас с тем, как добиться хорошего стиля программирования с помощью шаблонов проектирования для правильной организации. Здесь мы можем увидеть использование интерпретаторов, компоновщиков, фабричных методов и фабрик в среде Spring. Во-первых, помочь интерпретировать текст, выраженный в SpEL. Три последних шаблона относятся к трем мушкетерам создания шаблонов проектирования, основная цель которых в Spring — упростить создание объектов. Делают создание объектов декомпозируя инициализацию сложных объектов (билдеров) или фокусируясь на инициализации в общей точке (иначе что такое фабрика, должна быть общая точка).