Паттерн Singleton своими словами.

Привет всем, javaRush!

Расскажу сегодня про паттерн проектирования Singleton(одиночка).

Цель:
Создать класс, у которого будет только ОДИН объект.

Это значит, что сколько бы раз к нему не обращались, возвращаться будет один и тот же объект, который был создан первый раз.

Это удобная вещь и необходимая во многих местах, не зря ее внедряют во фреймворки.

Применение
  • Например необходимо подключить базу данных в проект и класс, который будет отвечать за соединение с ней. Один раз создается соединение и нет нужны создавать его снова и снова
  • Application settings — класс отвечающий за настройки отружения, которые нужны для приложения: хост и порт базы данных и т.д. Они создаются один раз и используются всё время работы приложения.
  • есть еще множество примеров, о которых я не сказал, поэтому пишите в комментариях свои варианты! =)


  • После этого вступления, как я понимаю можно показать уже пример этого класса: (Хотя я уверен, что каждый из нас сможет придумать реализацию этого)
    Вот самый простой пример, когда мы ставим приватным конструктор, т.е. нельзя создавать явно объект. И есть статический метод getInstance(), который предоставляет объект.
    
    public class Singleton {
      private static Singleton instance;
      private Singleton () {}
    
      public static Singleton getInstance() {
        if (instance == null) {
          instance = new Singleton();
        }
        return instance;
      }
    }
    

    Есть проблемы с многопоточностью и тогда можно поставить метод getInstance() маркер synchronized:
    
    public class Singleton {
      private static Singleton instance;
      private Singleton () {}
    
      public static synchronized Singleton getInstance() {
        if (instance == null) {
          instance = new Singleton();
        }
        return instance;
      }
    }
    


    В конце, как обычно, хочу сказать, что если вы думаете иначе или нашли у меня ошибку — пишите в комментариях! Мы все обсудим, с удовольствием :)

    Если Вам понравилась статья, пишите "+" и я буду это знать. Это для меня важно :)


P.S. Добавляю еще реализации:

По мнению Joshua Bloch’а это лучший способ реализации шаблона Enum Singleton

public enum Singleton {
	INSTANCE;
}


Double Checked Locking & volatile

public class Singleton {
        private static volatile Singleton instance;
	
        public static Singleton getInstance() {
		Singleton localInstance = instance;
		if (localInstance == null) {
			synchronized (Singleton.class) {
				localInstance = instance;
				if (localInstance == null) {
					instance = localInstance = new Singleton();
				}
			}
		}
		return localInstance;
	}
}


И еще On Demand Holder idiom:

public class Singleton {
		
	public static class SingletonHolder {
		public static final Singleton HOLDER_INSTANCE = new Singleton();
	}
		
	public static Singleton getInstance() {
		return SingletonHolder.HOLDER_INSTANCE;
	}
}

+ Ленивая инициализация
+ Высокая производительность
— Невозможно использовать для не статических полей класса

Будут вопросы/предложения — пишите в комментарии!


См. также мои другие статьи:
Тестовое задание: «Написать Интерпретатор на язык BrainFuck»
Тестовое задание «Image Comparison»
Java — быстрее, сильнее и выше! Зарплаты украинских программистов.
История успеха спустя 1.5 года от начала обучения
Технические вопросы на собеседовании.
Как найти работу? Рассылка резюме
Профессиональное выгорание. Как устоять?
Английский для IT и для собеседования
Паттерн Command своими словами.
Паттерн Singleton своими словами.
Как создать исполняемый jar в Intellij IDEA / how to create jar in IDEA
Помогите, нужна мотивация!

Подписывайтесь на мой блог Паттерны Проектирования пишите в нем статьи!

Паттерн Command своими словами.

Привет всем, Форумчане!


Разобрался-таки основательно(как мне кажется) с паттером Command и хочу попытаться рассказать о нем «своими словами».
Исходя из википедии, можем узнать, что цель его
Создание структуры, в которой класс-отправитель и класс-получатель не зависят друг от друга напрямую. Организация обратного вызова к классу, который включает в себя класс-отправитель.

В принципи написано чётко и правильно, но это в теории. А как же сделать это?
Вот этим начинаются проблемы, т.к. описание уже не так ясно и очевидно.
Поэтому я как разобрался, решил рассказать вам как я это понял, может кому-то и пригодится:

Исходя из описания цели этого паттерна, буду комбинировать описание сразу с кодом, чтоб было нагляднее, т.к. в той же википедии обобщили для многих языков и поэтому описание отделено от примера.

В этом паттерне есть четыре термина, пока примем их как данность: команды(command), приемник команд(receiver), вызывающий команды(invoker) и клиент(client).

Пример буду брать с той же википедии, он вполне сносный.

Задача
есть класс Light, который умеет две вещи: включить свет и выключить. Он в терминах паттерна будет «приемник команд(receiver)»
/*Receiver class*/

public class Light{
     public Light(){  }
   
     public void turnOn(){
        System.out.println("The light is on");
     }

     public void turnOff(){
        System.out.println("The light is off");
     }
}


Создадим интерфейс с одним методом execute(), который будет выполнять и который называется в терминах паттерна «команда(commadn)»
/*the Command interface*/

public interface Command{
    void execute();
}


Необходимо инкапсулировать выполнение умений класса Light. Для этого мы создадим классы TurnOnLightCommand и TurnOffLightCommand, которые реализуют интерфейс Command и которые будут принимать в конструкторе объект класса Light. И каждый из них будет выполнять только одно действие. Один будет вызывать метод turnOn(), а другой turnOff().
/*the Command for turning on the light*/

public class TurnOnLightCommand implements Command{
   private Light theLight;

   public TurnOnLightCommand(Light light){
        this.theLight=light;
       }

   public void execute(){
      theLight.turnOn();
   }
}

/*the Command for turning off the light*/

public class TurnOffLightCommand implements Command{
   private Light theLight;

   public TurnOffLightCommand(Light light){
        this.theLight=light;
       }

   public void execute(){
      theLight.turnOff();
   }
}


Теперь пришло время создать объект, который бы принимал эти инкапсулированные методы объекта Light. Он в терминах паттерна называется вызывающий команды (invoker). Назовем его Switch и пусть он будет принимать в конструкторе переменные Command, которые будут использоваться в созданных методах flipUp() и flipDown().
/*the Invoker class*/

public class Switch {
    private Command flipUpCommand;
    private Command flipDownCommand;

    public Switch(Command flipUpCommand,Command flipDownCommand){
         this.flipUpCommand=flipUpCommand;
         this.flipDownCommand=flipDownCommand;
    }

    public void flipUp(){
         flipUpCommand.execute();
    }
    
    public void flipDown(){
         flipDownCommand.execute();
    }
}


Ну и конечно создадим класс который будет использовать их, чтобы понять что происходит вообще. Он будет именть метод main, в котором и будет происходить всё действие:
/*The test class*/
public class TestCommand{
   public static void main(String[] args){
       // создаем объект, который будет использоваться
       Light l=new Light();
       // создаем объекты для всех умений объекта Light:
       Command switchUp=new TurnOnLightCommand(l);
       Command switchDown=new TurnOffLightCommand(l);
        
       // Создаемтся invoker, с которым мы будем непосредственно контактировать:
       Switch s=new Switch(switchUp,switchDown);
       
       // вот проверка этого, используем методы:
       s.flipUp();
       s.flipDown();
   }
}

На выводе будет следующее:

"The light is on"
"The light is off"


Где это применяется?
По цели ясно, что и для чего это нужно, а именно: в ситуации, когда нужно отделить конкретное исполнение, это очень удобно. Чтоб использование каких-то функций не зависело от конкретной реализации и ее можно было изменять без ущерба для системы.
как-то так...)
Пишите свои комментарии, давайте обсуждать, может что-то можно сделать проще и рассказать лучше, будем всё править, если необходимо) Чтоб для тех, кто будет читать первый раз, было как можно понятнее.

Ну и кому понравится статья — ставьет "+" на ней :) Для меня это важно)


Со временем хочу написать еще про Builder, Singleton и прочие.


См. также мои другие статьи:
Тестовое задание: «Написать Интерпретатор на язык BrainFuck»
Тестовое задание «Image Comparison»
Java — быстрее, сильнее и выше! Зарплаты украинских программистов.
История успеха спустя 1.5 года от начала обучения
Технические вопросы на собеседовании.
Как найти работу? Рассылка резюме
Профессиональное выгорание. Как устоять?
Английский для IT и для собеседования
Паттерн Command своими словами.
Паттерн Singleton своими словами.
Как создать исполняемый jar в Intellij IDEA / how to create jar in IDEA
Помогите, нужна мотивация!

Подписывайтесь на мой блог Паттерны Проектирования пишите в нем статьи!