Различные реализации одноэлементного шаблона на разных языках

Python Go
Различные реализации одноэлементного шаблона на разных языках

предисловие

использовал некоторое время назадPythonКогда бизнес реализуется, яма находится, а точнее, дляPythonЯма, на которую непрофессионалу легко ступить;

Примерный код выглядит следующим образом:

class Mom(object):
    name = ''
    sons = []

if __name__ == '__main__':
    m1 = Mom()
    m1.name = 'm1'
    m1.sons.append(['s1', 's2'])
    print '{} sons={}'.format(m1.name, m1.sons)

    m2 = Mom()
    m2.name = 'm2'
    m2.sons.append(['s3', 's4'])
    print '{} sons={}'.format(m2.name, m2.sons)

Сначала определитеMomкласс, содержащий строку типаnameс типом спискаsonsАтрибуты;

Экземпляр класса сначала создается при использованииm1и идиsonsЗаписывает список данных, затем создает экземплярm2, также кsonsЗаписываются данные другого списка.

еслиJavaerредко пишешьPythonПервый вывод, который приходит на ум, когда вы видите подобный код, должен быть таким:

m1 sons=[['s1', 's2']]
m2 sons=[['s3', 's4']]

Но на самом деле окончательный вывод:

m1 sons=[['s1', 's2']]
m2 sons=[['s1', 's2'], ['s3', 's4']]

Если вы хотите добиться желаемого значения, вам нужно немного изменить его:

class Mom(object):
    name = ''

    def __init__(self):
        self.sons = []

Просто нужно изменить определение класса, и я считаю, что даже безPythonВы должны быть в состоянии угадать причину, сравнив эти два кода с соответствующим опытом:

существуетPythonЕсли вам нужно использовать переменные в качестве переменных экземпляра (то есть каждый ожидаемый вывод), вам нужно определить переменные в конструкторе черезselfдоступ.

если размещать только в классе, иJavaсерединаstaticСтатические переменные имеют аналогичный эффект, эти данные являются общими для классов, что объясняет, почему происходит первый случай, так как один изsonsКMomКлассы общие, поэтому они будут накапливаться каждый раз.

Синглтон Python

теперь, когдаPythonЭффект от совместного использования переменных в одном классе может быть достигнут с помощью переменных класса.Возможна ли реализация шаблона singleton?

годный к употреблениюPythonизmetaclassЭта функция динамически управляет созданием классов.

class Singleton(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

Сначала создайтеSingletonБазовый класс , который мы затем используем какmetaclass

class MySQLDriver:
    __metaclass__ = Singleton

    def __init__(self):
        print 'MySQLDriver init.....'

такSingletonможет контролироватьMySQLDriverЭтот класс создан; фактически, вSingletonсередина__call__Процесс создания этого синглтона можно легко понять:

  • Определить свойство частного класса_instancesсловарь (т.Javaсерединаmap) может быть общим для всего класса, независимо от того, сколько экземпляров создано.
  • Когда наш пользовательский класс использует__metaclass__ = SingletonПосле этого вы можете управлять созданием пользовательских классов, если экземпляр был создан, непосредственно из_instancesВыньте объект и верните, в противном случае создайте экземпляр и запишите его обратно_instances, немногоSpringОщущение контейнера.
if __name__ == '__main__':
    m1 = MySQLDriver()
    m2 = MySQLDriver()
    m3 = MySQLDriver()
    m4 = MySQLDriver()
    print m1
    print m2
    print m3
    print m4

MySQLDriver init.....
<__main__.MySQLDriver object at 0x10d848790>
<__main__.MySQLDriver object at 0x10d848790>
<__main__.MySQLDriver object at 0x10d848790>
<__main__.MySQLDriver object at 0x10d848790>

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

Перейти синглтон

Поскольку некоторые предприятия в команде недавно начали использоватьgo, так что я тоже хочу увидетьgoКак реализовать синглтон в .

type MySQLDriver struct {
	username string
}

В такой простой структуре (которую можно просто понимать какJavaсерединаclass) не похож наPythonа такжеJavaТакже возможно объявить общие переменные класса;goне существует в языкеstaticКонцепция чего-либо.

Но мы можем объявить глобальную переменную в пакете для достижения того же эффекта:

import "fmt"

type MySQLDriver struct {
	username string
}

var mySQLDriver *MySQLDriver

func GetDriver() *MySQLDriver {
	if mySQLDriver == nil {
		mySQLDriver = &MySQLDriver{}
	}
	return mySQLDriver
}

При использовании этого:

func main() {
	driver := GetDriver()
	driver.username = "cj"
	fmt.Println(driver.username)

	driver2 := GetDriver()
	fmt.Println(driver2.username)

}

не нужно строить напрямуюMySQLDriver, но черезGetDriver()функция получения черезdebugтакже можно увидетьdriverа такжеdriver1Ссылка на тот же адрес памяти.

В реализации такой рутинной ситуации нет проблем, а остроумные друзья обязательно придумают иJavaОпять же, когда-то одновременный доступ не так прост.

существуетgo, если их несколькоgoroutineОдновременный доступGetDriver(), то велика вероятность того, что несколькоMySQLDriverпример.

Здесь все не так просто, это на самом деле относительноJavaГоворя о,goпростоapiДоступ к критически важным ресурсам может быть обеспечен.

var lock sync.Mutex

func GetDriver() *MySQLDriver {
	lock.Lock()
	defer lock.Unlock()
	if mySQLDriver == nil {
		fmt.Println("create instance......")
		mySQLDriver = &MySQLDriver{}
	}
	return mySQLDriver
}

func main() {
	for i := 0; i < 100; i++ {
		go GetDriver()
	}

	time.Sleep(2000 * time.Millisecond)
}

Немного изменил приведенный выше код и добавил

lock.Lock()
defer lock.Unlock()

Код может просто контролировать доступ к критическим ресурсам, даже если мы разрешим одновременное выполнение 100 сопрограмм,mySQLDriverЭкземпляры также инициализируются только один раз.

  • здесьdeferпохожий наJavaсерединаfinally, добавить перед вызовом методаgoключевое слово для запуска сопрограммы.

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

mySQLDriver = &MySQLDriver{}

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

Такие сценарии одинаковы для каждого языка, возьмемJavaНапример, вы часто видите такую ​​реализацию синглтона:

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

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

то же самое дляgoПохожий на:

func GetDriver() *MySQLDriver {
	if mySQLDriver == nil {
		lock.Lock()
		defer lock.Unlock()
		if mySQLDriver == nil {
			fmt.Println("create instance......")
			mySQLDriver = &MySQLDriver{}
		}
	}
	return mySQLDriver
}

а такжеJavaТаким же образом, вынесение дополнительного суждения на исходной основе может привести к тому же результату.

Но не кажется ли вам, что такой код очень громоздкий, этотgoкоторый предоставилapiЭто очень просто:

var once sync.Once

func GetDriver() *MySQLDriver {
	once.Do(func() {
		if mySQLDriver == nil {
			fmt.Println("create instance......")
			mySQLDriver = &MySQLDriver{}
		}
	})
	return mySQLDriver
}

По сути, нам просто нужно, независимо от ситуацииMySQLDriverЭкземпляр инициализируется только один раз для достижения цели синглтона, поэтому используйтеonce.Do()Это позволяет выполнить код только один раз.

Глядя на исходный код, вы найдетеonce.Do()Это также реализовано через блокировки, но базовая атомарная операция используется для выполнения проверки перед блокировкой, чтобы избежать блокировки каждый раз, и производительность будет лучше.

Суммировать

Я считаю, что в повседневной разработке вы редко столкнетесь с необходимостью реализовать синглтон самостоятельно, во-первых, в большинстве случаев синглтон нам не нужен, а даже если и требуется, то фреймворк обычно интегрирован.

похожий наgoТаким образом, меньше фреймворков, и нам не нужно слишком много думать о параллелизме, когда нам нужно реализовать его самим; почувствуйте положение в верхней левой части нашего живота и подумайте об этом, этот объект, написанный нами, действительно имеет сотни или тысячи одновременных творений одновременно.

Однако это сравнение показывает, чтоgoСинтаксис действительно лучше, чемJavaЭто слишком лаконично, и в то же время легковесные сопрограммы и простая в использовании поддержка инструментов параллелизма кажутся лучше, чемJavaГораздо элегантнее; последующие возможности пойти глубже.

Ссылка на ссылку:

Creating a singleton in Python

How to implement Singleton Pattern in Go