• Познакомьтесь с пентестом веб-приложений на практике в нашем новом бесплатном курсе

    «Анализ защищенности веб-приложений»

    🔥 Записаться бесплатно!

  • CTF с учебными материалами Codeby Games

    Обучение кибербезопасности в игровой форме. Более 200 заданий по Active Directory, OSINT, PWN, Веб, Стеганографии, Реверс-инжинирингу, Форензике и Криптографии. Школа CTF с бесплатными курсами по всем категориям.

Singleton паттерн

  • Автор темы NikSoft
  • Дата начала
N

NikSoft

В моей предыдущей заметке( Single Instance Application ) было показано как строить приложение, только единичная копия которого находится в памяти в любой момент времени. Продолжим эту же тему по отношению к типу.
Пусть нам нужен централизованный доступ к базе данных, причем необходимо иметь только один обьект класса, реализующий этот доступ. Следующий класс GetInfoFromDB решает задачу.

Код:
using System.Data;
using System.Data.SqlClient;

namespace Singleton
{
class GetInfoFromDB
{
static GetInfoFromDB _instance = null;

protected GetInfoFromDB(){ } // the constructor

static GetInfoFromDB Instance
{
get
{
if (_instance == null) _instance = new GetInfoFromDB();
return _instance;
}
}

static SqlConnection Connection
{
get 
{
return new SqlConnection("Data Source=(local);Initial Catalog=Costupdate;Integrated Security=True;");
}
}

DataTable GetCategoriesFromDB()
{
DataSet dataSet = new DataSet();

Connection.Open();
SqlDataAdapter adapter = new SqlDataAdapter("SELECT * FROM Categories;", Connection);
adapter.Fill(dataSet, "Categories");
Connection.Close();

return dataSet.Tables["Categories"];
}

public static DataTable GetCategories()
{
return Instance.GetCategoriesFromDB();
}

DataTable GetManufacturersFromDB()
{
DataSet dataSet = new DataSet();

Connection.Open();
SqlDataAdapter adapter = new SqlDataAdapter("SELECT * FROM Manufacturers;", Connection);
adapter.Fill(dataSet, "Manufacturers");
Connection.Close();

return dataSet.Tables["Manufacturers"];
}

public static DataTable GetManufacturers()
{
return Instance.GetManufacturersFromDB();
}
}
}

Ввиду того, что спецификатор доступа конструктора класса – protected, доступ к классу обеспечивается только
через статические методы GetCategories, GetManufacturers,которые являются публичными(public).
При вызове этих методов(как, например, в следующем коде)

Код:
dataGridView1.DataSource = new DataView(GetInfoFromDB.GetCategories());
dataGridView2.DataSource = new DataView(GetInfoFromDB.GetManufacturers());

происходит обращение к свойству Instance класса. Это свойство проверяет, был ли создан обьект
класса GetInfoFromDB. Если нет, то создается обьект типа GetInfoFromDB, ссылка на который присваивается приватному статическому полю _instance. В любом случае возвращается ссылка только на один и тот же обьект типа GetInfoFromDB.
 
K

karlito

Для: NikSoft
Оценка 2 за этот код.

Код:
if (_instance == null) _instance = new GetInfoFromDB();
return _instance;
Данный код непотокобезопасен.

Код:
Connection.Open();
SqlDataAdapter adapter = new SqlDataAdapter("SELECT * FROM Categories;", Connection);
adapter.Fill(dataSet, "Categories");
Connection.Close();
соединение гарантировано закрывать надо?

PS:
1. люди учатся по твоему коду и учатся неправильно, что самое страшное
2. любой шаблон предназаначен для решения неких проблем. именно с этого надо было начинать.
Для чего ты у себя используешь этот шаблон в коде не совсем понятно.
3. есть некий неформальное соглашение по кодированию шаблонов. везде функционал по предоставлению Singleton объекта отдаётся клиенту через метод Instance(), а не свойство.
 
E

eisernWolf

>>Данный код непотокобезопасен.

А что, кто-то обещал безопасность при мультитрединге? :)

>>через метод Instance(), а не свойство.

get_Instance() тоже ничего себе метод ;)
 
@

@Egot

public static GetInfoFromDB Instance = new GetInfoFromDB();

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

Pasha

есть еще вариант c
Код:
private static object instanceLock = new object();

public static Instance
{
get
{
lock (instanceLock)
{
if (instance == null)
instance = new ...
}
}
}

В догонку: ,
 
K

karlito

Решение, испытанное годами.

Код:
internal class AddInHolder
{
private AddInHolder(){}

#region Singleton

private static volatile AddInHolder _instance = null;
private static object _syncRoot = new object();

internal static AddInHolder Instance()
{
if((_instance == null))
{
lock(_syncRoot)
{
if((_instance == null))
{
_instance = new AddInHolder();
}
}
}
return _instance;
}
#endregion
}

Для: Pasha
Не надо лочить объекты без нужды - крепче спать будешь.
 
P

Pasha

2 karlito: Я же дал ссылку, там есть этот вариант. И результаты тестов. Вариант с двойной проверкой работает на 1% быстрее :)
 
K

karlito

Для: Pasha
2 karlito: Я же дал ссылку, там есть этот вариант. И результаты тестов. Вариант с двойной проверкой работает на 1% быстрее
Прогони этот тест на 1000 потоков одновременно. я думаю процент вырастет и намного.
 
P

Pasha

Согласен, был неправ :D Проверил: 1000 потоков, каждый занят только тем, что получает значение Instance. Двойная проверка работает примерно в 2 раза быстрее. Но если потоки при этом занять еще и чем-то другим, то разница в производительности сразу исчезает.
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!