데이터베이스 연결을위한 글로벌 또는 싱글 톤?


81

PHP에서 데이터베이스 연결에 전역 대신 싱글 톤을 사용하면 어떤 이점이 있습니까? 글로벌 대신 싱글 톤을 사용하면 코드가 불필요하게 복잡해집니다.

글로벌 코드

$conn = new PDO(...);

function getSomething()
{
    global $conn;
    .
    .
    .
}

싱글 톤 코드

class DB_Instance
{
    private static $db;

    public static function getDBO()
    {
        if (!self::$db)
            self::$db = new PDO(...);

        return self::$db;
    }
}

function getSomething()
{
    $conn = DB_Instance::getDBO();
    .
    .
    .
}

글로벌 또는 싱글 톤 이외의 데이터베이스 연결을 초기화하는 더 좋은 방법이 있다면이를 언급하고 글로벌 또는 싱글 톤에 비해 장점을 설명하십시오.


사용자 지정 세션 처리기에서 PDO를 사용하려는 경우 몇 가지 특성을 알고 있어야합니다. stackoverflow.com/questions/2595860/pdo-prepare-silently-fails
Wabbitseason

답변:


105

나는 이것이 오래되었다는 것을 알고 있지만 Dr8k의 대답은 거의 다 왔습니다 .

코드 작성을 고려할 때 변경 될 것이라고 가정하십시오. 그것은 당신이 미래의 어느 시점에서 그것에 대한 변화의 종류를 가정하고 있다는 것을 의미하는 것이 아니라 어떤 형태의 변화가 이루어질 것이라는 것을 의미합니다.

미래의 변화에 ​​따른 고통을 덜어주는 목표로 삼으십시오. 글로벌은 단일 지점에서 관리하기가 어렵 기 때문에 위험합니다. 나중에 데이터베이스 연결 컨텍스트를 인식하게하려면 어떻게해야합니까? 5 번 사용할 때마다 자동으로 닫히고 다시 열리도록하려면 어떻게해야합니까? 앱 확장을 위해 10 개의 연결 풀을 사용하기로 결정하면 어떻게됩니까? 아니면 구성 가능한 연결 수?

싱글 공장은 당신에게 그 유연성을 제공합니다. 나는 거의 추가로 복잡하지 않게 설정하고 동일한 연결에 대한 액세스 이상의 것을 얻습니다. 나중에 간단한 방법으로 그 연결이 나에게 전달되는 방식을 변경할 수 있습니다.

나는 단순히 싱글 톤이 아니라 싱글 톤 팩토리 를 말한다 . 싱글 톤과 글로벌, 사실 사이에는 아주 작은 차이가 있습니다. 그렇기 때문에 싱글 톤 연결을 가질 이유가 없습니다. 대신 일반 글로벌을 만들 수 있는데 왜 설정하는 데 시간을 소비하겠습니까?

공장이 당신을 얻는 것은 연결을 얻는 이유이며, 어떤 연결 (또는 연결)을 얻을 것인지를 결정하는 별도의 지점입니다.

class ConnectionFactory
{
    private static $factory;
    private $db;

    public static function getFactory()
    {
        if (!self::$factory)
            self::$factory = new ConnectionFactory(...);
        return self::$factory;
    }

    public function getConnection() {
        if (!$this->db)
            $this->db = new PDO(...);
        return $this->db;
    }
}

function getSomething()
{
    $conn = ConnectionFactory::getFactory()->getConnection();
    .
    .
    .
}

그런 다음 6 개월 동안 앱이 매우 유명하고 더그와 슬래시 점이 찍히고 하나 이상의 연결이 필요하다고 결정하면 getConnection () 메서드에서 풀링을 구현하기 만하면됩니다. 또는 SQL 로깅을 구현하는 래퍼를 원하는 경우 PDO 하위 클래스를 전달할 수 있습니다. 또는 모든 호출에 대해 새로운 연결을 원하면 그렇게 할 수 있습니다. 단단하지 않고 유연합니다.

중괄호를 포함한 16 줄의 코드를 사용하면 몇 시간, 몇 시간, 몇 시간의 리팩토링 시간을 절약 할 수 있습니다.

첫 번째 라운드에서 기능 구현을 수행하지 않기 때문에이 "기능 크리프"를 고려하지 않습니다. "Future Creep"의 경계선이지만 어느 시점에서 "오늘 내일을위한 코딩"이 항상 나쁜 일이라는 생각이 나에게 맞지 않습니다.


3
확실하지는 않지만 다음을 의미한다고 생각합니다. public function getConnection () {if (! $ this-> db) $ this-> db = new PDO (...); return $ this-> db; }
Dycey 2010-08-03

감사! 내가 사용하여이 방법을 사용의 혜택을 잃게 return self::$factory->getConnection();대신 return self::$factory;?
Nico Burns

3
현재 진행중인 프로젝트에서이 코드를 사용하고 싶습니다. 이 페이지를 인용 할 수 있습니까? 그렇다면이 텍스트에는 어떤 라이선스가 적용됩니까? CC-BY, BSD 또는 다른 것입니까? 현재 "알 수 없음-공개 도메인 믿기"라고 주장했지만 올바른 라이선스 조건을 설명하고 싶습니다.
JonTheNiceGuy

2
getConnection () 메서드에서 "$ db" "$ this-> db"를 만들어야한다고 생각합니다. 그렇지 않으면 "private $ db"변수가 "사용되지 않습니다". 공식적으로 어디에도 참조되지 않습니다.
developer10

안녕하세요 귀하의 솔루션은 멋지고 확장 가능해 보입니다. 여기에서 구현해야 할 사항을 알려주세요 self :: $ factory = new ConnectionFactory (...);
아난다

16

특정 질문에 대답 할 수 있을지 모르겠지만 웹 기반 시스템의 경우 전역 / 단일 연결 개체가 최상의 아이디어가 아닐 수 있음을 제안하고 싶었습니다. DBMS는 일반적으로 많은 수의 고유 한 연결을 효율적으로 관리하도록 설계되었습니다. 전역 연결 개체를 사용하는 경우 다음 두 가지 작업을 수행합니다.

  1. 페이지가 모든 데이터베이스 연결을 순차적으로 수행하도록 강제하고 비동기 페이지로드 시도를 중단합니다.

  2. 잠재적으로 데이터베이스 요소에 대한 열린 잠금을 필요 이상으로 오래 유지하여 전체 데이터베이스 성능을 저하시킵니다.

  3. 데이터베이스가 지원할 수있는 총 동시 연결 수를 최대화하고 새 사용자가 리소스에 액세스하는 것을 차단합니다.

다른 잠재적 인 결과도있을 것이라고 확신합니다. 이 방법은 사이트에 액세스하는 모든 사용자에 대해 데이터베이스 연결을 유지하려고 시도합니다. 사용자가 한 명 또는 두 명뿐이라면 문제가되지 않습니다. 이것이 공개 웹 사이트이고 트래픽을 원하면 확장 성이 문제가 될 것입니다.

[편집하다]

규모가 더 큰 상황에서는 데이터에 도달 할 때마다 새로운 연결을 만드는 것이 좋지 않을 수 있습니다. 그러나 대답은 전역 연결을 만들고 모든 것에 재사용하는 것이 아닙니다. 대답은 연결 풀링입니다.

연결 풀링을 사용하면 여러 고유 연결이 유지됩니다. 애플리케이션에 연결이 필요한 경우 풀에서 사용 가능한 첫 번째 연결이 검색된 다음 작업이 완료되면 풀로 반환됩니다. 연결이 요청되었지만 사용 가능한 연결이없는 경우 다음 두 가지 중 하나가 발생합니다. .

참고 : .Net 언어에서 연결 풀링은 기본적으로 ADO.Net 개체에 의해 처리됩니다 (연결 문자열은 모든 필수 정보를 설정 함).

이에 대해 논평 해 주신 Crad에게 감사드립니다.


싱글 톤을 사용하면 가능한 한 늦게 연결을 초기화 할 수있는 이점이있는 것 같지만 글로벌을 사용하면 스크립트 시작 부분에서 거의 초기화 할 것입니다. 제가 맞습니까?
Imran

맞습니다, 만약 당신이이 길을 가면 싱글 톤이 그 이점을 제공 할 것입니다.
Dr8k

실제로 모든 것은 규모에 달려 있습니다. 대규모 웹 배포에서는 엄청난 양의 DB 연결이 악합니다. 이것이 PostgreSQL 용 pgBouncer와 같은 앱이 존재하고 Java가 리소스 풀링을 수행하는 이유입니다.
Gavin M. Roy

사실이지만 적절한 연결 풀링은 전역 연결 개체를 사용하는 것과 다릅니다. 연결 풀링은 여전히 ​​여러 연결을 사용하지만 최대 수를 제한하고 시간이 지남에 따라 다시 사용하여 설정 오버 헤드를 줄입니다.
Dr8k

5
PHP의 경우 "global"은 PHP 페이지 전체에서 변수를 전역으로 만들지 않습니다. 함수 내에서 액세스 할 수 있음을 의미합니다.
Ates Goral

7

싱글 톤 메서드는 클래스의 인스턴스가 하나만 있는지 확인하기 위해 만들어졌습니다. 그러나 사람들이이를 글로벌화를 단축하는 방법으로 사용하기 때문에 게 으르거나 나쁜 프로그래밍으로 알려지게됩니다.

따라서 둘 다 실제로 OOP가 아니기 때문에 global과 Singleton을 무시합니다.

당신이 찾고 있던 것은 의존성 주입 입니다.

http://components.symfony-project.org/dependency-injection/trunk/book/01-Dependency-Injection 에서 종속성 주입과 관련된 읽기 쉬운 PHP 기반 정보 (예제 포함)를 확인할 수 있습니다 .


3

두 패턴 모두 데이터베이스 호출에 대해 하나의 단일 액세스 포인트를 제공하여 동일한 효과를 얻습니다.

특정 구현 측면에서 싱글 톤은 다른 메소드 중 적어도 하나가 요청할 때까지 데이터베이스 연결을 시작하지 않는 작은 이점이 있습니다. 실제로 내가 작성한 대부분의 응용 프로그램에서 이것은 큰 차이를 만들지 않지만 데이터베이스 호출을 전혀하지 않는 일부 페이지 / 실행 경로가있는 경우 잠재적 인 이점입니다. 데이터베이스에 대한 연결을 요청하십시오.

또 다른 사소한 차이점은 전역 구현이 의도하지 않게 응용 프로그램의 다른 변수 이름을 짓밟을 수 있다는 것입니다. 실수로 덮어 쓸 수도 있지만 실수로 다른 전역 $ db 참조를 선언 할 가능성은 거의 없습니다 (예 : if ($ db == null)를 작성하려고 할 때 if ($ db = null)을 작성합니다). 싱글 톤 객체는이를 방지합니다.


2

지속적인 연결을 사용하지 않을 것이고 그렇게하지 않는 경우가 있다면, 저는 OO 디자인에서 글로벌보다 개념적으로 더 좋은 싱글 톤을 발견했습니다.

진정한 OO 아키텍처에서는 매번 새로운 인스턴스를 생성하는 것보다 싱글 톤이 더 효과적입니다.


2

주어진 예에서 싱글 톤을 사용할 이유가 없습니다. 경험상 유일한 관심사가 객체의 단일 인스턴스를 허용하는 것이라면 언어가 허용하는 경우 전역을 사용하는 것을 선호합니다.


1

일반적으로 저는 데이터베이스 연결에 싱글 톤을 사용합니다. 데이터베이스와 상호 작용해야 할 때마다 새 연결을 생성하고 싶지 않습니다. 네트워크의 성능과 대역폭이 손상 될 수 있습니다. 새 것, 사용 가능한 경우 ... 내 2 센트 만 ...

RWendi


전역 범위에서 연결을 초기화하면서 페이지 당 한 번 연결을 초기화하고 데이터베이스와 상호 작용해야하는 함수에서 해당 전역 변수를 사용합니다.
Imran

데이터베이스 연결에 싱글 톤을 사용하는 것은 DBMS와의 각 상호 작용에서 연결을 다시 생성하지 않는 것과 동일하지 않습니다. 싱글 톤은 바로 그것입니다. 주어진 클래스의 하나의 인스턴스와 전 세계적으로 존재할 수있는 유일한 인스턴스입니다. 한 번에 다른 데이터베이스에 연결해야 할 수도 있습니다.
Rob

나는 데이터베이스에 대한 연결을 관리하는 싱글 톤 클래스를 언급하고있었습니다. dbms와 상호 작용할 때마다 새 연결 개체를 만드는 요점을 보지 못했습니다. 물론 한 번에 다른 데이터베이스에 연결해야하는 경우 다른 연결 개체를 만들어야 할 수도 있습니다.
RWendi

0

아주 간단합니다. 글로벌 또는 싱글 톤을 사용하지 마십시오.


4
Singleton 패턴에 관해서는 항상 말하지 마십시오
1800 INFORMATION

3
둘 이상의 로그 공급자를 원하면 어떻게해야합니까? 누가 파일과 콘솔에 로그인 할 수 없다고 말합니까?
1800 정보

2
그런 다음 하나의 안티 패턴 (Singleton)과 다른 (God Object)을 결합합니다
1800 INFORMATION

3
예. 그리고 글로벌 클래스 객체를 허용함으로써 모든 주요 OOP 언어의 디자이너도 마찬가지입니다. 만약 당신이 일신 론자이고 신을 대표하는 물체를 원한다면, 당신이 찾고있는 것은 Singleton God Object입니다. 다른 것은 올바르지 않습니다.
Steve Jessop

4
내가 월간 자 였고 God 객체를 만들고 싶었다면 MonotheistAbstractFactory 패턴을 사용하여 God 객체를 생성하도록 지정할 수 있습니까? 이것은 다신 론적 사용자가 PolytheistAbstractfactory를 지정하여 내 프로그램을 사용할 수있는 가능성을 열어줍니다.
1800 정보

0

싱글 톤글로벌 조언이 모두 유효하고 동일한 시스템, 프로젝트, 플러그인, 제품 등 내에서 결합 될 수 있으므로 ... 제 경우에는 웹 (플러그인) 용 디지털 제품을 만듭니다.

메인 클래스 에서는 싱글 톤 만 사용하고 원칙적으로 사용합니다. 메인 클래스가 다시 인스턴스화하지 않을 것이라는 것을 알고 있기 때문에 거의 사용하지 않습니다.

<?php // file0.php

final class Main_Class
{
    private static $instance;
    private $time;

    private final function __construct()
    {
        $this->time = 0;
    }
    public final static function getInstance() : self
    {
        if (self::$instance instanceof self) {
            return self::$instance;
        }

        return self::$instance = new self();
    }
    public final function __clone()
    {
        throw new LogicException("Cloning timer is prohibited");
    }
    public final function __sleep()
    {
        throw new LogicException("Serializing timer is prohibited");
    }
    public final function __wakeup()
    {
        throw new LogicException("UnSerializing timer is prohibited");
    }
}

거의 모든 보조 클래스에 대한 전역 사용 예 :

<?php // file1.php
global $YUZO;
$YUZO = new YUZO; // YUZO is name class

런타임에 Global 을 사용 하여 동일한 인스턴스에서 메서드와 속성을 호출 할 수 있습니다 . 주 제품 클래스의 다른 인스턴스가 필요하지 않기 때문입니다.

<?php // file2.php
global $YUZO;
$YUZO->method1()->run();
$YUZO->method2( 'parameter' )->html()->print();

나는 동일한 클래스의 인스턴스에 대한 팩토리가 필요하지 않기 때문에 동일한 인스턴스를 사용하여 제품을 작동시킬 수 있습니다. 일반적으로 인스턴스 팩토리는 대규모 시스템 또는 매우 드문 목적입니다.

In conclusion:, 당신이 이미 안티 패턴 싱글 톤 인 것을 잘 이해하고 Global을 이해 했다면, 두 가지 옵션 중 하나를 사용하거나 혼합 할 수 있지만, 예외적이고 충실한 프로그래머가 많기 때문에 남용하지 말 것을 권장한다면 프로그래밍 OOP, 실행 시간 내에 많이 사용하는 기본 및 보조 클래스에 사용하십시오. (많은 CPU를 절약합니다). 😉

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.