한 걸음 물러서서 더 큰 그림을 보자.
IDatabase
책임 은 무엇입니까 ?
몇 가지 다른 작업이 있습니다.
- 연결 문자열 구문 분석
- 데이터베이스 (외부 시스템)와의 연결을 엽니 다
- 데이터베이스에 메시지를 보냅니다. 메시지는 데이터베이스에 상태를 변경하도록 명령합니다.
- 데이터베이스에서 응답을 수신하여 발신자가 사용할 수있는 형식으로 변환
- 연결을 닫습니다
이 목록을 보면 "이것이 SRP를 위반하지 않습니까?" 그러나 나는 그렇게 생각하지 않습니다. 모든 작업은 단일 통합 개념의 일부입니다. 즉 , 데이터베이스 (외부 시스템)에 대한 상태 저장 연결을 관리합니다 . 연결을 설정하고 (특히 다른 연결에서 수행 된 작업과 관련하여) 연결의 현재 상태를 추적하고 연결의 현재 상태를 커밋 할시기를 알립니다. 이러한 의미에서 API 역할을합니다. 대부분의 발신자가 신경 쓰지 않는 많은 구현 세부 사항을 숨 깁니다. 예를 들어 HTTP, 소켓, 파이프, 사용자 지정 TCP, HTTPS를 사용합니까? 호출 코드는 중요하지 않습니다. 단지 메시지를 보내고 응답을 받기를 원합니다. 이것은 캡슐화의 좋은 예입니다.
확실합니까? 이러한 작업 중 일부를 분리 할 수 없습니까? 어쩌면 이점은 없습니다. 그것들을 분리하려고하면 연결을 계속 유지하거나 현재 상태를 관리하는 중앙 객체가 여전히 필요합니다. 다른 모든 작업은 동일한 상태에 강력하게 연결되어 있으며 분리하려고하면 어쨌든 연결 개체로 다시 위임됩니다. 이러한 작업은 자연스럽고 논리적으로 상태 와 연결되어 있으며 분리 할 방법이 없습니다. 디커플링은 우리가 할 수있을 때 훌륭하지만,이 경우 실제로 는. 적어도 DB와 통신하는 매우 다른 상태 비 저장 프로토콜이 없으면 ACID 준수와 같은 매우 중요한 문제가 실제로 훨씬 더 어려워집니다. 또한 이러한 작업을 연결에서 분리하려고 시도하는 동안 일종의 "임의"메시지를 보내는 방법이 필요하므로 호출자가 신경 쓰지 않는 프로토콜에 대한 세부 정보를 노출해야합니다. 데이터베이스에.
우리가 상태 저장 프로토콜을 다루고 있다는 사실은 마지막 대안 (연결 문자열을 매개 변수로 전달)을 거의 확실하게 배제합니다.
연결 문자열을 설정해야합니까?
예. 당신은 할 수없는 열 이 연결 문자열을 때까지 연결하고 연결을 열 때까지 당신이 프로토콜을 사용하여 아무것도 할 수 없습니다. 그건 그래서 무의미 하나없이 연결 개체를 가지고.
연결 문자열이 필요한 문제를 어떻게 해결합니까?
우리가 해결하려는 문제는 객체가 항상 사용 가능한 상태가되기를 원한다는 것입니다. OO 언어로 상태를 관리하는 데 어떤 종류의 엔티티가 사용됩니까? 인터페이스가 아닌 객체 . 인터페이스는 관리 할 상태가 없습니다. 해결하려는 문제는 상태 관리 문제이므로 인터페이스가 실제로 적합하지 않습니다. 추상 클래스는 훨씬 더 자연 스럽습니다. 따라서 생성자와 함께 추상 클래스를 사용하십시오.
연결을 열기 전에는 쓸모가 없기 때문에 생성자 동안 실제로 연결을 여는 것도 고려할 수 있습니다 . protected Open
연결을 여는 프로세스는 데이터베이스마다 다를 수 있으므로 추상적 인 방법 이 필요합니다 . ConnectionString
연결이 열린 후 연결 문자열을 변경하는 것은 의미가 없으므로이 경우 속성을 읽기 전용 으로 설정하는 것이 좋습니다 . (정직하게, 나는 어쨌든 읽기 전용으로 만들 것입니다. 다른 문자열로 연결하려면 다른 객체를 만드십시오.)
우리는 전혀 인터페이스가 필요합니까?
연결을 통해 보낼 수있는 사용 가능한 메시지와 다시받을 수있는 응답 유형을 지정하는 인터페이스가 유용 할 수 있습니다. 이를 통해 이러한 작업을 실행하는 코드를 작성할 수 있지만 연결을 여는 논리와는 관련이 없습니다. 그러나 요점은 연결 관리가 "어떤 메시지를 보낼 수 있고 어떤 메시지를 데이터베이스와주고받을 수 있는가?"라는 인터페이스의 일부가 아니기 때문에 연결 문자열이 그 일부가되어서는 안된다는 것입니다. 상호 작용.
이 경로를 사용하면 코드가 다음과 같이 보일 수 있습니다.
interface IDatabase {
void ExecuteNoQuery(string sql);
void ExecuteNoQuery(string[] sql);
//Various other methods all requiring ConnectionString to be set
}
abstract class ConnectionStringDatabase : IDatabase {
public string ConnectionString { get; }
public Database(string connectionString) {
this.ConnectionString = connectionString;
this.Open();
}
protected abstract void Open();
public abstract void ExecuteNoQuery(string sql);
public abstract void ExecuteNoQuery(string[] sql);
//Various other methods all requiring ConnectionString to be set
}