предисловие
Привет всем, ямаленький мальчик собирает улиток.
Обычно мы пишем код, в большинстве случаевтрубопроводНаписав код, вы можете реализовать бизнес-логику.Как получать удовольствие от написания кода, я думаю, лучший способ:Используйте шаблоны проектирования для оптимизации вашего бизнес-кода. Сегодня я расскажу вам о том, какие шаблоны проектирования я использовал в своей повседневной работе.
- Общий номер галантереи:маленький мальчик собирает улиток
1. Режим стратегии
1.1 Бизнес-сценарий
Предположим, есть такой бизнес-сценарий, система больших данных проталкивает файлы и берет разные типы файлов.другой разборСпособ. Большинство мелких партнеров напишут следующий код:
if(type=="A"){
//按照A格式解析
}else if(type=="B"){
//按B格式解析
}else{
//按照默认格式解析
}
Что это может быть за кодкак насчет проблемы?
- Если веток больше, код здесь станетРаздутый, сложный в обслуживании и с низкой читабельностью.
- Если вам нужно получить доступ к новому типу синтаксического анализа, это можно сделать только вИзменить исходный код.
Говоря более профессионально, приведенный выше код нарушает принципы объектно-ориентированного программирования.принцип открыто-закрытоа такжеединый принцип.
- принцип открыто-закрыто(Открыто для расширения, но закрыто для модификации): Чтобы добавить или удалить определенную логику, необходимо изменить исходный код.
- единый принцип(Укажите, что у класса должна быть только одна причина для изменения): Изменение любого типа кода логики ветвления требует изменения кода текущего класса.
Если ваш код соусно-фиолетовый: есть несколькоif...elseи другие условные ветки, и каждая условная ветка может быть инкапсулирована и заменена, мы можем использоватьрежим стратегииоптимизировать.
1.2 Определение шаблона стратегии
режим стратегииСемейство алгоритмов определяется и инкапсулируется отдельно, чтобы их можно было заменять друг другом.Этот режим делает изменения алгоритма независимыми от клиентов, которые используют алгоритм. Не является ли определение этого шаблона стратегии немного абстрактным? Итак, давайте рассмотрим простую аналогию:
Предположим, вы встречаетесь с девушками с разными типами личности, вы должны использовать разные стратегии, некоторые лучше подходят для фильмов, некоторые хороши для закусок, а некоторые лучше всего подходят для покупок. Конечно, цель состоит в том, чтобы завоевать сердце юной леди.Пожалуйста, смотрите фильмы, ешьте закуски и ходите по магазинам - это разные стратегии.
Шаблон стратегии нацелен на набор алгоритмов, инкапсулируя каждый алгоритм в отдельный класс с общим интерфейсом, что делает их взаимозаменяемыми.
1.3 Использование режима стратегии
Как использовать режим стратегии? Цзян Цзы достиг:
- Интерфейс или абстрактный класс с двумя методами в нем (один метод, соответствующий типу, один альтернативный метод реализации логики)
- Дифференцированные реализации разных стратегий (то есть классы реализации разных стратегий)
- Использовать режим стратегии
1.3.1 Один интерфейс, два метода
public interface IFileStrategy {
//属于哪种文件解析类型
FileTypeResolveEnum gainFileType();
//封装的公用算法(具体的解析方法)
void resolve(Object objectparam);
}
1.3.2 Дифференцированное применение разных стратегий
Конкретная реализация стратегии типа А
@Component
public class AFileResolve implements IFileStrategy {
@Override
public FileTypeResolveEnum gainFileType() {
return FileTypeResolveEnum.File_A_RESOLVE;
}
@Override
public void resolve(Object objectparam) {
logger.info("A 类型解析文件,参数:{}",objectparam);
//A类型解析具体逻辑
}
}
Конкретная реализация стратегии типа B
@Component
public class BFileResolve implements IFileStrategy {
@Override
public FileTypeResolveEnum gainFileType() {
return FileTypeResolveEnum.File_B_RESOLVE;
}
@Override
public void resolve(Object objectparam) {
logger.info("B 类型解析文件,参数:{}",objectparam);
//B类型解析具体逻辑
}
}
Реализация стратегии типов по умолчанию
@Component
public class DefaultFileResolve implements IFileStrategy {
@Override
public FileTypeResolveEnum gainFileType() {
return FileTypeResolveEnum.File_DEFAULT_RESOLVE;
}
@Override
public void resolve(Object objectparam) {
logger.info("默认类型解析文件,参数:{}",objectparam);
//默认类型解析具体逻辑
}
}
1.3.3 Использование режима стратегии
Как это использовать? мы используемspringжизненный цикл, используяApplicationContextAwareинтерфейс, инициализируйте соответствующую стратегию дляmapв. а затем предоставитьresolveFileметод.
/**
* @author 公众号:捡田螺的小男孩
*/
@Component
public class StrategyUseService implements ApplicationContextAware{
private Map<FileTypeResolveEnum, IFileStrategy> iFileStrategyMap = new ConcurrentHashMap<>();
public void resolveFile(FileTypeResolveEnum fileTypeResolveEnum, Object objectParam) {
IFileStrategy iFileStrategy = iFileStrategyMap.get(fileTypeResolveEnum);
if (iFileStrategy != null) {
iFileStrategy.resolve(objectParam);
}
}
//把不同策略放到map
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, IFileStrategy> tmepMap = applicationContext.getBeansOfType(IFileStrategy.class);
tmepMap.values().forEach(strategyService -> iFileStrategyMap.put(strategyService.gainFileType(), strategyService));
}
}
2. Модель цепочки ответственности
2.1 Бизнес-сценарий
Давайте рассмотрим распространенный бизнес-сценарий, размещение заказа. Интерфейс заказа, основная логика, как правило, включает проверку ненулевого параметра, проверку безопасности, проверку черного списка, перехват правил и т. д. Многие партнеры будут использовать исключения для достижения:
public class Order {
public void checkNullParam(Object param){
//参数非空校验
throw new RuntimeException();
}
public void checkSecurity(){
//安全校验
throw new RuntimeException();
}
public void checkBackList(){
//黑名单校验
throw new RuntimeException();
}
public void checkRule(){
//规则拦截
throw new RuntimeException();
}
public static void main(String[] args) {
Order order= new Order();
try{
order.checkNullParam();
order.checkSecurity ();
order.checkBackList();
order2.checkRule();
System.out.println("order success");
}catch (RuntimeException e){
System.out.println("order fail");
}
}
}
Этот код используетаномальныйЧтобы сделать логические суждения об условиях, если последующая логика становится все более и более сложной, возникнут некоторые проблемы: например, исключения могут возвращать только информацию об исключении, но не могут возвращать больше полей.пользовательский класс исключений.
Кроме того, в руководстве по разработке на Али прописано:Запрещено использовать исключения для вынесения логических суждений.
【Обязательный】 Исключения не должны использоваться для контроля процесса, условного контроля.
Примечание. Первоначальная цель разработки исключений состоит в том, чтобы разрешать различные непредвиденные ситуации в работе программы, и эффективность обработки исключений намного ниже, чем эффективность оценки условий.
Как оптимизировать этот код? Можно рассмотреть этоМодель цепочки ответственности
2.2 Определение модели цепочки ответственности
когда вы хотите сделатьнад объектамиКогда есть возможность обработать запрос, используйтеМодель цепочки ответственности.
Шаблон цепочки ответственности создает цепочку объектов-получателей для запроса. В цепочке выполнения есть несколько узлов объектов, и каждый узел объекта имеет возможность (сопоставление условий) обработать транзакцию запроса.Если узел объекта обрабатывается, он может быть передан следующему узлу для продолжения обработки или возврата для обработки. в соответствии с реальными потребностями бизнеса. Этот шаблон указывает тип запроса, разделяя отправителя и получателя запроса.
Шаблон цепочки ответственности на самом деле представляет собой шаблон обработки запроса, который дает нескольким процессорам (объектным узлам) возможность обрабатывать запрос до тех пор, пока один из них не добьется успеха. Шаблон Chain of Responsibility связывает несколько процессоров в цепочку, а затем позволяет запросу пройти по цепочке:
Чтобы провести аналогию:
Допустим, вы идете вечером на факультатив и сидите в последнем ряду, чтобы можно было немного пройтись. Когда вы приходите в класс, вы видите, что перед вами сидят несколько красивых девушек, поэтому вы находите записку и пишете: «Здравствуйте, вы можете быть моей девушкой? Если вы не хотите, пожалуйста, перешлите». Записи передавались одна за другой, а потом перешли к девушке в первом ряду. Она передала записи учителю. Я слышал, что учительнице за 40, и она не замужем...
2.3 Использование модели цепочки ответственности
Как использовать модель цепочки ответственности?
- Интерфейс или абстрактный класс
- Дифференциация для каждого объекта
- Цепочка объектов (массив) инициализация (соединение)
2.3.1 Интерфейс или абстрактный класс
Этот интерфейс или абстрактный класс требует:
- имеет свойство, указывающее на следующий объект ответственности
- Метод set, который устанавливает следующий объект
- Метод для дифференциации реализации объектов подкласса (например, метод doFilter в следующем коде)
/**
* 关注公众号:捡田螺的小男孩
*/
public abstract class AbstractHandler {
//责任链中的下一个对象
private AbstractHandler nextHandler;
/**
* 责任链的下一个对象
*/
public void setNextHandler(AbstractHandler nextHandler){
this.nextHandler = nextHandler;
}
/**
* 具体参数拦截逻辑,给子类去实现
*/
public void filter(Request request, Response response) {
doFilter(request, response);
if (getNextHandler() != null) {
getNextHandler().filter(request, response);
}
}
public AbstractHandler getNextHandler() {
return nextHandler;
}
abstract void doFilter(Request filterRequest, Response response);
}
2.3.2 Дифференциальная обработка каждого объекта
В цепочке ответственности каждый объектДифференциацияДля обработки, такой как бизнес-сценарии в этом разделе, есть объекты проверки параметров, объекты проверки безопасности, объекты проверки черного списка и объекты перехвата правил.
/**
* 参数校验对象
**/
@Component
@Order(1) //顺序排第1,最先校验
public class CheckParamFilterObject extends AbstractHandler {
@Override
public void doFilter(Request request, Response response) {
System.out.println("非空参数检查");
}
}
/**
* 安全校验对象
*/
@Component
@Order(2) //校验顺序排第2
public class CheckSecurityFilterObject extends AbstractHandler {
@Override
public void doFilter(Request request, Response response) {
//invoke Security check
System.out.println("安全调用校验");
}
}
/**
* 黑名单校验对象
*/
@Component
@Order(3) //校验顺序排第3
public class CheckBlackFilterObject extends AbstractHandler {
@Override
public void doFilter(Request request, Response response) {
//invoke black list check
System.out.println("校验黑名单");
}
}
/**
* 规则拦截对象
*/
@Component
@Order(4) //校验顺序排第4
public class CheckRuleFilterObject extends AbstractHandler {
@Override
public void doFilter(Request request, Response response) {
//check rule
System.out.println("check rule");
}
}
2.3.3 Цепочка объектов (инициализация) и использование
@Component("ChainPatternDemo")
public class ChainPatternDemo {
//自动注入各个责任链的对象
@Autowired
private List<AbstractHandler> abstractHandleList;
private AbstractHandler abstractHandler;
//spring注入后自动执行,责任链的对象连接起来
@PostConstruct
public void initializeChainFilter(){
for(int i = 0;i<abstractHandleList.size();i++){
if(i == 0){
abstractHandler = abstractHandleList.get(0);
}else{
AbstractHandler currentHander = abstractHandleList.get(i - 1);
AbstractHandler nextHander = abstractHandleList.get(i);
currentHander.setNextHandler(nextHander);
}
}
}
//直接调用这个方法使用
public Response exec(Request request, Response response) {
abstractHandler.filter(request, response);
return response;
}
public AbstractHandler getAbstractHandler() {
return abstractHandler;
}
public void setAbstractHandler(AbstractHandler abstractHandler) {
this.abstractHandler = abstractHandler;
}
}
Результаты приведены ниже:
非空参数检查
安全调用校验
校验黑名单
check rule
3. Шаблон шаблонного метода
3.1 Бизнес-сценарий
Предположим, у нас такой бизнес-сценарий: разные мерчанты во внутренней системе вызывают интерфейс нашей системы для взаимодействия с внешней сторонней системой (режим http). Пройдите процесс, аналогичный этому, следующим образом:
Запрос проходит через следующие процессы:
- Запрос бизнес-информации
- Подпишите сообщение запроса
- отправить http запрос
- Проверьте возвращенное сообщение
Здесь некоторые торговцы могут выходить через агентов, а некоторые - через прямые связи. Если предположить, что в настоящее время есть доступ к продавцам A и B, многие партнеры могут достичь этого.Псевдокод выглядит следующим образом:
// 商户A处理句柄
CompanyAHandler implements RequestHandler {
Resp hander(req){
//查询商户信息
queryMerchantInfo();
//加签
signature();
//http请求(A商户假设走的是代理)
httpRequestbyProxy()
//验签
verify();
}
}
// 商户B处理句柄
CompanyBHandler implements RequestHandler {
Resp hander(Rreq){
//查询商户信息
queryMerchantInfo();
//加签
signature();
// http请求(B商户不走代理,直连)
httpRequestbyDirect();
// 验签
verify();
}
}
Предполагая, что добавлен новый продавец C, вам необходимо реализовать еще один набор таких кодов. Очевидно, этот код будетповторениев настоящее время,Некоторые общие методы, но перепишите этот метод в каждом подклассе.
Как его оптимизировать? можно использоватьШаблон метода шаблона.
3.2 Определение шаблона метода шаблона
Определить скелетный поток алгоритма в операции и передать некоторые шаги подклассам, позволяя подклассам переопределять определенные шаги алгоритма без изменения его структуры. Его основная идея состоит в том, чтобы определить серию шагов для операции.Для некоторых шагов, которые не могут быть определены временно, предоставьте их реализацию подклассам, чтобы разные подклассы могли определять разные шаги.
Проведем простую аналогию:
Пример шаблона: Чтобы преследовать девушку, вы должны сначала «взять за руки», затем «обнять», затем «поцеловать», а затем «погладить... э... руку». Что касается того, используете ли вы левую руку или правую руку, это не имеет значения, но для всего процесса задается шаблон процесса, и вы можете следовать шаблону.
3.3 Использование шаблонного метода
- Абстрактный класс, определяющий скелет процесса (абстрактные методы объединяются)
- Определены шаги общего метода, помещенные в абстрактный класс (удалить тег абстрактного метода)
- Неопределенные шаги, недифференцированная реализация для подклассов
Давайте продолжим приведенный выше пример бизнес-процесса и используем шаблонный метод для его совместной оптимизации:
3.3.1 Абстрактный класс, определяющий скелетный процесс
Потому что процесс каждого запроса состоит из следующих шагов:
- Запрос бизнес-информации
- Подпишите сообщение запроса
- отправить http запрос
- Проверьте возвращенное сообщение
Таким образом, мы можем определить абстрактный класс, который содержит несколько методов процесса запроса, методы сначала определяются как абстрактные методы:
/**
* 抽象类定义骨架流程(查询商户信息,加签,http请求,验签)
*/
abstract class AbstractMerchantService {
//查询商户信息
abstract queryMerchantInfo();
//加签
abstract signature();
//http 请求
abstract httpRequest();
// 验签
abstract verifySinature();
}
3.3.2 Определите общие шаги метода и поместите их в абстрактный класс
abstract class AbstractMerchantService {
//模板方法流程
Resp handlerTempPlate(req){
//查询商户信息
queryMerchantInfo();
//加签
signature();
//http 请求
httpRequest();
// 验签
verifySinature();
}
// Http是否走代理(提供给子类实现)
abstract boolean isRequestByProxy();
}
3.3.3 Неопределенные шаги, недифференцированная реализация для подклассов
Потому что стоит ли проходить через прокси-процесснеуверенныйДа, так что отдайте его подклассам для реализации.
Реализация запроса продавца А:
CompanyAServiceImpl extends AbstractMerchantService{
Resp hander(req){
return handlerTempPlate(req);
}
//走http代理的
boolean isRequestByProxy(){
return true;
}
Реализация запроса продавца B:
CompanyBServiceImpl extends AbstractMerchantService{
Resp hander(req){
return handlerTempPlate(req);
}
//公司B是不走代理的
boolean isRequestByProxy(){
return false;
}
4. Шаблон наблюдателя
4.1 Бизнес-сценарий
Вход и регистрация должны быть наиболее распространенным бизнес-сценарием. просто возьмирегистрКстати говоря, мы часто сталкиваемся с подобными сценариями, то есть после того, как пользователь успешно зарегистрирован, мы отправляем сообщение пользователю, или отправляем электронное письмо и т. д., поэтому часто возникает следующий код:
void register(User user){
insertRegisterUser(user);
sendIMMessage();
sendEmail();
}
Что не так с этим кодом? Если есть другой спрос на продукт: пользователь, который успешно зарегистрировался сейчас, отправит пользователю еще одно SMS-уведомление. Поэтому вам придется снова изменить код метода регистрации. . . Является ли это нарушениемпринцип открыто-закрытоЛа.
void register(User user){
insertRegisterUser(user);
sendIMMessage();
sendMobileMessage();
sendEmail();
}
А если настроитьСбой интерфейса для отправки текстовых сообщенийВлияет ли это снова на регистрацию пользователей? ! В настоящее время необходимо добавить асинхронный метод вуведомлениепросто хорошо. . .
На самом деле мы можем оптимизировать с помощью шаблона Observer.
4.2 Определение шаблона наблюдателя
Шаблон наблюдателя определяет зависимость между объектами по принципу «один ко многим».При изменении состояния объекта все зависящие от него объекты уведомляются и обновляются для завершения работы.
Режим наблюдателя относится к режиму поведения, при изменении состояния объекта (наблюдаемого) все зависимые объекты (объекты-наблюдатели) будут оповещаться и транслировать уведомление. Его основными членами являютсяНаблюдатель и наблюдаемый.
- Наблюдатель (Observerable): целевой объект будет уведомлять всех наблюдателей об изменении состояния.
- Наблюдатель (наблюдатель): получает уведомление об изменении состояния наблюдаемого объекта и выполняет заранее определенные действия.
используемые сцены:Асинхронно уведомлять сцену, когда что-то делается. Например, успешно войти в систему, отправить мгновенное сообщение и так далее.
4.3 Использование шаблона наблюдателя
Если шаблон наблюдателя реализован, это относительно просто.
- Observable класса Observable;
- несколько наблюдателей Наблюдатель;
- Дифференцированная реализация наблюдателей
- Инкапсуляция классического шаблона наблюдателя: EventBus в действии
4.3.1 Один наблюдаемый класс Наблюдаемый и несколько наблюдателей Наблюдатель
public class Observerable {
private List<Observer> observers
= new ArrayList<Observer>();
private int state;
public int getState() {
return state;
}
public void setState(int state) {
notifyAllObservers();
}
//添加观察者
public void addServer(Observer observer){
observers.add(observer);
}
//移除观察者
public void removeServer(Observer observer){
observers.remove(observer);
}
//通知
public void notifyAllObservers(int state){
if(state!=1){
System.out.println(“不是通知的状态”);
return ;
}
for (Observer observer : observers) {
observer.doEvent();
}
}
}
4.3.2 Дифференцированная реализация наблюдателей
//观察者
interface Observer {
void doEvent();
}
//Im消息
IMMessageObserver implements Observer{
void doEvent(){
System.out.println("发送IM消息");
}
}
//手机短信
MobileNoObserver implements Observer{
void doEvent(){
System.out.println("发送短信消息");
}
}
//EmailNo
EmailObserver implements Observer{
void doEvent(){
System.out.println("发送email消息");
}
}
4.3.3 Битва EventBus
Самостоятельно создать набор кода для шаблона наблюдателя все еще немного проблематично. По факту,Guava EventBus упаковано, это
Предоставляет набор шин событий на основе аннотаций, API можно использовать гибко, круто.
Давайте взглянемEventBusСобственно боевой код можно сначала объявить в классе EventBusCenter, роль которого аналогична рассмотренному вышеObserverable.
public class EventBusCenter {
private static EventBus eventBus = new EventBus();
private EventBusCenter() {
}
public static EventBus getInstance() {
return eventBus;
}
//添加观察者
public static void register(Object obj) {
eventBus.register(obj);
}
//移除观察者
public static void unregister(Object obj) {
eventBus.unregister(obj);
}
//把消息推给观察者
public static void post(Object obj) {
eventBus.post(obj);
}
}
Затем снова объявите наблюдателяEventListener
public class EventListener {
@Subscribe //加了订阅,这里标记这个方法是事件处理方法
public void handle(NotifyEvent notifyEvent) {
System.out.println("发送IM消息" + notifyEvent.getImNo());
System.out.println("发送短信消息" + notifyEvent.getMobileNo());
System.out.println("发送Email消息" + notifyEvent.getEmailNo());
}
}
//通知事件类
public class NotifyEvent {
private String mobileNo;
private String emailNo;
private String imNo;
public NotifyEvent(String mobileNo, String emailNo, String imNo) {
this.mobileNo = mobileNo;
this.emailNo = emailNo;
this.imNo = imNo;
}
}
Тест с демо:
public class EventBusDemoTest {
public static void main(String[] args) {
EventListener eventListener = new EventListener();
EventBusCenter.register(eventListener);
EventBusCenter.post(new NotifyEvent("13372817283", "123@qq.com", "666"));
}
}
результат операции:
发送IM消息666
发送短信消息13372817283
发送Email消息123@qq.com
5. Заводской узор
5.1 Бизнес-сценарий
Фабричный паттерн обычно используется в сочетании с паттерном стратегии. используется для оптимизации большого количестваif...else...илиswitch...case...Условные утверждения.
Возьмем пример шаблона стратегии из первого подраздела. Создавайте различные объекты синтаксического анализа в соответствии с различными типами синтаксического анализа файлов.
IFileStrategy getFileStrategy(FileTypeResolveEnum fileType){
IFileStrategy fileStrategy ;
if(fileType=FileTypeResolveEnum.File_A_RESOLVE){
fileStrategy = new AFileResolve();
}else if(fileType=FileTypeResolveEnum.File_A_RESOLV){
fileStrategy = new BFileResolve();
}else{
fileStrategy = new DefaultFileResolve();
}
return fileStrategy;
}
На самом деле этозаводской узор, определите интерфейс для создания объекта и позвольте его подклассам решать, какой фабричный класс создавать.
В примере паттерна стратегии не используется предыдущий код, а используются характеристики spring для создания фабричного паттерна, ха-ха, вы можете вернуться к этому примеру и рассмотреть его поближе, я сдвину код вниз, а вы сможете попробуйте еще раз.
/**
* @author 公众号:捡田螺的小男孩
*/
@Component
public class StrategyUseService implements ApplicationContextAware{
private Map<FileTypeResolveEnum, IFileStrategy> iFileStrategyMap = new ConcurrentHashMap<>();
//把所有的文件类型解析的对象,放到map,需要使用时,信手拈来即可。这就是工厂模式的一种体现啦
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, IFileStrategy> tmepMap = applicationContext.getBeansOfType(IFileStrategy.class);
tmepMap.values().forEach(strategyService -> iFileStrategyMap.put(strategyService.gainFileType(), strategyService));
}
}
5.2 Использование заводского шаблона
Определение фабричного шаблона также относительно просто:
- Фабричный интерфейс, предоставляющий метод для создания различных объектов.
- Его подклассы реализуют фабричный интерфейс для создания различных объектов.
- Использовать заводской шаблон
5.3.1 Заводской интерфейс
interface IFileResolveFactory{
void resolve();
}
5.3.2 Различные подклассы реализуют фабричный интерфейс
class AFileResolve implements IFileResolveFactory{
void resolve(){
System.out.println("文件A类型解析");
}
}
class BFileResolve implements IFileResolveFactory{
void resolve(){
System.out.println("文件B类型解析");
}
}
class DefaultFileResolve implements IFileResolveFactory{
void resolve(){
System.out.println("默认文件类型解析");
}
}
5.3.3 Использование заводского шаблона
//构造不同的工厂对象
IFileResolveFactory fileResolveFactory;
if(fileType=“A”){
fileResolveFactory = new AFileResolve();
}else if(fileType=“B”){
fileResolveFactory = new BFileResolve();
}else{
fileResolveFactory = new DefaultFileResolve();
}
fileResolveFactory.resolve();
Обычно для заводского шаблона приведенный выше код не отображается. Фабричный шаблон появится вместе с другими шаблонами проектирования, такими как шаблон стратегии.
6. Одноэлементный шаблон
6.1 Бизнес-сценарий
одноэлементный узор,Гарантирует, что существует только один экземпляр классаи предоставить к нему глобальную точку доступа. Связь между вводом-выводом и базой данных обычно реализуется в одноэлементном режиме. Диспетчер задач в Windows также является типичным одноэлементным режимом.
Давайте посмотрим на пример одноэлементного шаблона
/**
* 公众号:捡田螺的小男孩
*/
public class LanHanSingleton {
private static LanHanSingleton instance;
private LanHanSingleton(){
}
public static LanHanSingleton getInstance(){
if (instance == null) {
instance = new LanHanSingleton();
}
return instance;
}
}
Приведенный выше примерленивыйреализация синглтона. Экземпляры создаются только тогда, когда они нужны, что лениво. Если есть, верните его, если нет, создайте новый, нужно добавить следующееsynchronized ключевое слово, иначе оно может существоватьЛинейная задача безопасности.
6.2 Классическое написание одноэлементного шаблона
На самом деле существует несколько реализаций одноэлементного режима, таких как голодный режим, блокировка с двойной проверкой, статический внутренний класс, перечисление и другие реализации.
6.2.1 Режим голодного человека
public class EHanSingleton {
private static EHanSingleton instance = new EHanSingleton();
private EHanSingleton(){
}
public static EHanSingleton getInstance() {
return instance;
}
}
режим голодного человека, этоболее голодный, более трудолюбивыйэкземпляр уже создан, когда он инициализируется.Используете ли вы его позже или нет, сначала создайте новый экземпляр. Проблем с безопасностью потоков нет, но это пустая трата памяти.
6.2.2 Двойной замок
public class DoubleCheckSingleton {
private static DoubleCheckSingleton instance;
private DoubleCheckSingleton() { }
public static DoubleCheckSingleton getInstance(){
if (instance == null) {
synchronized (DoubleCheckSingleton.class) {
if (instance == null) {
instance = new DoubleCheckSingleton();
}
}
}
return instance;
}
}
Одноэлементный режим, реализованный замком с двойной проверкой, сочетает в себе преимущества и недостатки как ленивого, так и голодного стилей. В приведенном выше примере кода слой добавляется внутри и снаружи ключевого слова synchronized.if Условное суждение, которое не только обеспечивает потокобезопасность, но также повышает эффективность выполнения по сравнению с прямой блокировкой и экономит место в памяти.
6.2.3 Статические внутренние классы
public class InnerClassSingleton {
private static class InnerClassSingletonHolder{
private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
private InnerClassSingleton(){}
public static final InnerClassSingleton getInstance(){
return InnerClassSingletonHolder.INSTANCE;
}
}
Реализация статического внутреннего класса чем-то похожа на блокировку с двойной проверкой. Однако этот метод подходит только для сценариев со статическим доменом, а метод блокировки с двойной проверкой можно использовать, когда необходимо лениво инициализировать домен экземпляра.
6.2.4 Перечисление
public enum SingletonEnum {
INSTANCE;
public SingletonEnum getInstance(){
return INSTANCE;
}
}
Синглтон реализации перечисления, код лаконичен и понятен. Кроме того, он автоматически поддерживает механизм сериализации, полностью предотвращая создание нескольких экземпляров.