모범 사례에서 건조 원칙?


11

가능한 한 열심히 프로그래밍에서 DRY 원칙을 따르려고합니다. 최근에는 OOP에서 디자인 패턴을 배우고 있었고 꽤 많은 것을 반복했습니다.

지속성을 처리하기 위해 팩토리 및 게이트웨이 패턴과 함께 리포지토리 패턴을 만들었습니다. 내 응용 프로그램에서 데이터베이스를 사용하고 있지만 원하는 경우 게이트웨이를 교체하고 다른 종류의 지속성으로 전환 할 수 있기 때문에 중요하지 않습니다.

내가 스스로 만들었던 문제는 내가 가지고있는 테이블 수에 대해 동일한 객체를 생성한다는 것입니다. 예를 들어 이들은 테이블을 처리하는 데 필요한 개체 comments입니다.

class Comment extends Model {

    protected $id;
    protected $author;
    protected $text;
    protected $date;
}

class CommentFactory implements iFactory {

    public function createFrom(array $data) {
        return new Comment($data);
    }
}

class CommentGateway implements iGateway {

    protected $db;

    public function __construct(\Database $db) {
        $this->db = $db;
    }

    public function persist($data) {

        if(isset($data['id'])) {
            $sql = 'UPDATE comments SET author = ?, text = ?, date = ? WHERE id = ?';
            $this->db->prepare($sql)->execute($data['author'], $data['text'], $data['date'], $data['id']);
        } else {
            $sql = 'INSERT INTO comments (author, text, date) VALUES (?, ?, ?)';
            $this->db->prepare($sql)->execute($data['author'], $data['text'], $data['date']);
        }
    }

    public function retrieve($id) {

        $sql = 'SELECT * FROM comments WHERE id = ?';
        return $this->db->prepare($sql)->execute($id)->fetch();
    }

    public function delete($id) {

        $sql = 'DELETE FROM comments WHERE id = ?';
        return $this->db->prepare($sql)->execute($id)->fetch();
    }
}

class CommentRepository {

    protected $gateway;
    protected $factory;

    public function __construct(iFactory $f, iGateway $g) {
        $this->gateway = $g;
        $this->factory = $f;
    }

    public function get($id) {

        $data = $this->gateway->retrieve($id);
        return $this->factory->createFrom($data);
    }

    public function add(Comment $comment) {

        $data = $comment->toArray();
        return $this->gateway->persist($data);
    }
}

그런 다음 컨트롤러가

class Comment {

    public function view($id) {

        $gateway = new CommentGateway(Database::connection());
        $factory = new CommentFactory();
        $repo = new CommentRepository($factory, $gateway);

        return Response::view('comment/view', $repo->get($id));
    }
}

따라서 디자인 패턴을 올바르게 사용하고 모범 사례를 유지하고 있다고 생각했지만이 문제는 새 테이블을 추가 할 때 다른 이름으로 동일한 클래스를 만들어야한다는 것입니다. 이것은 내가 뭔가 잘못하고 있다는 의심을 불러 일으킨다.

인터페이스 대신 클래스 이름을 사용하여 조작 해야하는 테이블을 알아내는 추상 클래스가있는 솔루션을 생각했지만 올바른 것으로 보이지 않습니다. 파일 저장소로 전환하기로 결정한 경우 또는 테이블이없는 memcache.

내가 이것에 올바르게 접근하고 있습니까, 아니면 내가보아야 할 다른 관점이 있습니까?


새 테이블을 작성할 때 항상 동일한 SQL 쿼리 세트 (또는 매우 유사한 세트)를 사용하여 테이블과 상호 작용합니까? 또한 팩토리는 실제 프로그램에서 의미있는 논리를 캡슐화합니까?
Ixrec

@Ixrec은 일반적으로 게이트웨이와 저장소에 조인과 같은 더 복잡한 SQL 쿼리를 수행하는 사용자 정의 메소드가 있으며, 문제는 인터페이스에 의해 정의 된 지속, 검색 및 삭제 기능이 테이블 이름을 제외하고 항상 동일하다는 것입니다. 기본 키 열은 거의 없으므로 모든 구현에서 반복해야합니다. 팩토리는 로직을 거의 보유하지 않으며 때로는 로직을 전혀 건너 뛰고 게이트웨이가 데이터 대신 객체를 반환하도록했지만이 예제의 팩토리를 만들었으므로 적절한 디자인 이어야 합니까?
Emilio Rodrigues

아마도 적절한 대답을 할 자격이 없지만 1) 팩토리 및 리포지토리 클래스가 실제로 유용한 것을 수행하지 못한다는 인상을 얻습니다. 따라서 수업을 버리고 Comment 및 CommentGateway와 직접 작업하는 것이 좋습니다. 2) 공통 지속 / 검색 / 삭제 기능을 복사 붙여 넣기 대신 한 장소에, 아마도 "기본 구현"의 추상 클래스 (Java의 콜렉션과 유사한 기능)에
넣을 수 있어야합니다.

답변:


12

해결하려는 문제는 매우 근본적입니다.

수백 개의 웹 페이지와 백만 및 반 줄 이상의 Java 코드로 구성된 큰 J2EE 응용 프로그램을 만든 회사에서 일할 때도 같은 문제가 발생했습니다. 이 코드는 지속성을 위해 ORM (JPA)을 사용했습니다.

아키텍처의 모든 계층에서 타사 기술을 사용하고 기술에 모두 고유 한 데이터 표현이 필요한 경우이 문제가 악화됩니다.

사용중인 프로그래밍 언어 수준에서 문제를 해결할 수 없습니다. 패턴을 사용하는 것이 좋지만, 코드 반복이 발생합니다 (더 정확하게 말하면 디자인 반복).

내가 보는 방식에는 3 가지 가능한 솔루션 만 있습니다. 실제로 이러한 솔루션은 동일합니다.

해결 방법 1 : 만 명시 할 수 있도록 다른 퍼시스턴스 프레임 워크 무엇을 지속해야합니다. 아마도 그런 프레임 워크가있을 것입니다. 이 접근 방식의 문제점은 모든 패턴이 지속성과 관련이 없기 때문에 다소 순진하다는 것입니다. 또한 사용자 인터페이스 코드에 패턴을 사용해야하므로 선택한 지속성 프레임 워크의 데이터 표현을 재사용 할 수있는 GUI 프레임 워크가 필요합니다. 재사용 할 수없는 경우 GUI 프레임 워크와 지속성 프레임 워크의 데이터 표현을 연결하기 위해 보일러 플레이트 코드를 작성해야합니다. 이는 다시 DRY 원칙과 상반됩니다.

해결 방법 2 : 디자인 코드를 재사용 할 수 있도록 반복적 인 디자인을 표현할 수있는 구문이있는보다 강력한 프로그래밍 언어를 사용하십시오. 이것은 아마도 당신을위한 옵션이 아니지만 잠시 동안 있다고 가정합니다. 그런 다음 지속성 레이어 위에 사용자 인터페이스를 만들기 시작할 때 보일러 플레이트 코드를 작성하지 않고도 GUI 작성을 지원할 수있을만큼 언어가 다시 강력 해지기를 원할 것입니다. 대부분의 언어는 각자 고유 한 데이터 표현이 필요한 GUI 구축을 위해 타사 프레임 워크에 의존하기 때문에 원하는 것을 수행 할 수있는 강력한 언어가 없을 것 같습니다.

해결 방법 3 : 일부 코드 생성 형식을 사용하여 코드 및 디자인 반복을 자동화합니다. 수작업 코딩 반복 코드 / 디자인이 DRY 원칙을 위반하므로 패턴과 디자인의 반복을 수작업 코딩해야하는 것에 대해 걱정해야합니다. 오늘날에는 매우 강력한 코드 생성기 프레임 워크가 있습니다. 심지어 "언어 워크 벤치"도 있습니다. (언어 경험이없는 날 반나절) 자신 만의 프로그래밍 언어를 만들고 해당 언어를 사용하여 코드 (PHP / Java / SQL-생각할 수있는 텍스트 파일)를 생성 할 수 있습니다. XText에 대한 경험이 있지만 MetaEdit 및 MPS도 좋습니다. 이러한 언어 워크 벤치 중 하나를 확인하는 것이 좋습니다. 나에게 그것은 내 직업 생활에서 가장 해방 된 경험이었습니다.

Xtext를 사용하면 컴퓨터에서 반복 코드를 생성 할 수 있습니다. Xtext는 자신의 언어 사양에 맞는 코드 완성 기능을 갖춘 구문 강조 편집기도 생성합니다. 이 시점에서 게이트웨이와 팩토리 클래스를 가져 와서 구멍을 뚫어 코드 템플릿으로 바꾸십시오. Xtext에 의해 완전히 생성 된 언어 파서에서 생성기로 생성기를 생성하면 생성기가 템플릿의 구멍을 채 웁니다. 결과는 코드가 생성됩니다. 그 시점부터 어디서나 코드 반복을 수행 할 수 있습니다 (GUI 코드 지속성 코드 등).


답장을 보내 주셔서 감사합니다. 코드 생성을 진지하게 고려했으며 솔루션을 구현하기 시작했습니다. 그들은 4 개의 상용구 클래스이므로 PHP 자체에서 그렇게 할 수 있다고 생각합니다. 이것이 반복되는 코드 문제를 해결하지는 못하지만 반복적 인 코드를 사용하더라도 유지 관리가 쉽고 쉽게 변경할 수있는 트레이드 오프가 가치 있다고 생각합니다.
Emilio Rodrigues

이것은 내가 XText에 대해 들어 본 첫 번째이며 매우 강력 해 보입니다. 이것을 알고 감사합니다!
Matthew James Briggs

8

당신이 직면하는 문제는 오래된 것입니다. 영속 객체에 대한 코드는 종종 각 클래스마다 비슷해 보이며 단순히 단순한 코드입니다. 그래서 일부 똑똑한 사람들이 Object Relational Mappers를 발명 한 이유는 바로 그 문제를 해결하는 것입니다. PHP 용 ORM 목록은 이전 SO 게시물 을 참조하십시오 .

기존 ORM이 요구 사항을 충족시키지 못하는 경우 대안도 있습니다. 고유 한 코드 생성기를 작성하여 객체에 대한 메타 설명을 유지하고 해당 코드에서 반복되는 부분을 생성 할 수 있습니다. 실제로 그렇게 어렵지는 않습니다. 과거에 다른 프로그래밍 언어를 위해 이것을 했으므로 PHP에서도 그러한 것들을 구현하는 것이 가능할 것이라고 확신합니다.


이러한 기능을 만들었지 만 데이터 개체가 SRP를 준수하지 않는 데이터 지속성 작업을 처리하도록했기 때문에이 기능에서이 기능으로 전환했습니다. 예를 들어 Model::getByPK방법을 사용 했지만 위의 예 Comment::getByPK에서는 데이터베이스에서 데이터를 가져오고 객체를 구성하는 것이 모두 데이터 객체 클래스에 포함되어 있습니다. 이는 디자인 패턴을 사용하여 해결하려는 문제입니다. .
Emilio Rodrigues

ORM은 모델 객체에 지속성 로직을 배치하지 않아도됩니다. 이것은 액티브 레코드 패턴이며 인기가 있지만 대안이 있습니다. 사용 가능한 ORM을 살펴보고이 문제가없는 ORM을 찾으십시오.
Jules

@Jules는 매우 좋은 지적입니다. 제 생각에 궁금한 점이 있습니다. 응용 프로그램에서 ActiveRecord 및 Data Mapper 구현을 모두 사용하는 데 어떤 문제가 있는지 궁금합니다. 나는이 때이 후, 액티브 패턴을 사용하여 동일한 코드를 rewritting의 내 문제를 해결합니다 - 내가 그들을 필요로 할 때 그 때 나는 그 하나 하나를 사용할 수 actuallly 필요한 데이터가 필요한 클래스를 만들 수있는 그런 고통되지 않을 것 매퍼 직업을 위해?
Emilio Rodrigues

1
내가 지금 볼 수있는 유일한 문제는 쿼리가 Active Record를 사용하고 다른 하나는 Data Mapper에 의해 관리되는 두 테이블을 조인 해야하는 경우를 처리하는 가장 큰 경우를 해결하는 것입니다. 발생하지 않습니다. 개인적으로, 나는 매퍼를 사용하고 싶었습니다. 처음부터 Active Record를 좋아 한 적이 없었습니다. 그러나 저는 그것이 저의 의견 일 뿐이고 다른 사람들은 동의하지 않는다는 것을 알고 있습니다.
Jules
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.