함수가 매개 변수를 수정해도 괜찮습니까?


17

Linq To SQL을 래핑하는 데이터 계층이 있습니다. 이 데이터 레이어에는이 방법이 있습니다 (간체 화됨)

int InsertReport(Report report)
{
    db.Reports.InsertOnSubmit(report);
    db.SubmitChanges();
    return report.ID; 
}

제출 변경 사항이 있으면 보고서 ID가 데이터베이스의 값으로 업데이트 된 다음 반환됩니다.

발신 측에서 다음과 같이 보입니다 (간체).

var report = new Report();
DataLayer.InsertReport(report);
// Do something with report.ID

코드를 살펴보면 InsertReport 함수 내에 ID가 일종의 부작용으로 설정되어 반환 값을 무시하고 있습니다.

내 질문은 부작용에 의존하고 대신 이와 같은 일을해야한다는 것입니다.

void InsertReport(Report report)
{
    db.Reports.InsertOnSubmit(report);
    db.SubmitChanges();
}

아니면 우리는 그것을 막아야

int InsertReport(Report report)
{
    var newReport = report.Clone();
    db.Reports.InsertOnSubmit(newReport);
    db.SubmitChanges();
    return newReport.ID; 
}

어쩌면

Report InsertReport(Report report)
{
    var newReport = report.Clone();
    db.Reports.InsertOnSubmit(newReport);
    db.SubmitChanges();
    return newReport; 
}

이 질문은 단위 테스트를 만들 때 발생했으며 보고서 매개 변수 ID 속성이 업데이트되고 부작용 동작을 조롱하는 것이 잘못되었다는 사실이 명확하지 않다는 것을 알았습니다.


2
이것이 API 문서입니다.

답변:


16

예, 괜찮습니다. 상당히 흔합니다. 당신이 발견 한 것처럼, 그것은 명백하지 않을 수 있습니다.

일반적으로 지속성 메서드가 업데이트 된 개체 인스턴스를 반환하는 경향이 있습니다. 그건:

Report InsertReport(Report report)
{        
    db.Reports.InsertOnSubmit(report);
    db.SubmitChanges();
    return report; 
}

예, 전달한 것과 동일한 객체를 반환하지만 API가 더 명확합니다. 복제 할 필요가 없습니다. 원래 코드에서와 같이 호출자가 전달한 객체를 계속 사용하는 경우 혼동을 유발할 수있는 항목이있는 경우.

다른 옵션은 DTO를 사용하는 것입니다

Report InsertReport(ReportDTO dto)
{
    var newReport = Report.Create(dto);
    db.Reports.InsertOnSubmit(newReport);
    db.SubmitChanges();
    return newReport; 
}

그렇게하면 API가 매우 분명하며 호출자가 실수로 전달 된 / 수정 된 객체를 시도하고 사용할 수 없습니다. 코드가 수행하는 작업에 따라 약간의 고통이 될 수 있습니다.


Ira가 필요하지 않으면 객체를 반환하지 않습니다. 추가 처리 및 메모리 사용 (과잉)처럼 보입니다. 필요하지 않은 경우, 공허 또는 예외가 있습니다. 그렇지 않으면, 귀하의 DTO 샘플에 투표합니다.
독립

이 모든 대답은 우리 자신의 토론을 요약합니다. 이것은 실제 답변이없는 질문 인 것 같습니다. "예, 전달한 것과 동일한 객체를 반환하지만 API가 더 명확 해집니다." 우리의 질문에 거의 대답했습니다.
John Petrak

4

IMO 변경의 부작용이 바람직한 경우는 드문 경우입니다. 보고서 엔터티에 ID가 있으므로 이미 DTO에 대한 우려가있는 것으로 간주 될 수 있으며 인 메모리 보고서를 보장 해야하는 ORM 의무 가있을 수 있습니다. 엔티티는 데이터베이스 표시 오브젝트와 동기화되어 유지됩니다.


6
+1-DB에 엔티티를 삽입 한 후 ID가 있을 것으로 예상 합니다. 엔티티가 없으면 엔티티가 다시 돌아 오는 것이 더 놀라운 것입니다.
MattDavey

2

문제는 문서화가 없다면 메소드가 무엇을하고 있는지, 특히 왜 정수를 반환하는지 명확하지 않다는 것입니다.

가장 쉬운 해결책은 분석법에 다른 이름을 사용하는 것입니다. 다음과 같은 것 :

int GenerateIdAndInsert(Report report)

그럼에도 불구하고 이것은 명확하지 않습니다. C # 에서처럼 report개체 의 인스턴스 가 참조로 전달되면 원본 report개체가 수정되었는지 또는 메서드가 복제하고 복제본 만 수정 했는지 알기가 어렵습니다 . 원본 객체를 수정하기로 선택한 경우 메소드 이름을 지정하는 것이 좋습니다.

void ChangeIdAndInsert(Report report)

더 복잡하고 최적의 솔루션은 코드를 크게 리팩토링하는 것입니다. 는 어때:

using (var transaction = new TransactionScope())
{
    var id = this.Data.GenerateReportId(); // We need to find an available ID...
    this.Data.AddReportWithId(id, report); // ... and use this ID to insert a report.
    transaction.Complete();
}

2

일반적으로 프로그래머는 객체의 인스턴스 메소드 만 상태를 변경할 수 있다고 기대합니다. 즉, report.insert()보고서의 ID 가 변경 되어도 놀라지 않으며 쉽게 확인할 수 있습니다. 보고서의 ID가 변경되는지 아닌지 전체 응용 프로그램의 모든 방법에 대해 궁금해하는 것은 쉽지 않습니다.

나는 또한 아마도 ID전혀 속하지 않아야 한다고 주장 할 것이다 Report. 유효한 ID가 오랫동안 포함되어 있지 않기 때문에 삽입 전후에 두 가지 다른 객체가 있으며 동작이 다릅니다. "이전"개체는 삽입 할 수 있지만 검색, 업데이트 또는 삭제할 수는 없습니다. "after"객체는 정반대입니다. 하나는 ID를 가지고 있고 다른 하나는 ID를 가지고 있지 않습니다. 표시되는 방식이 다를 수 있습니다. 표시되는 목록이 다를 수 있습니다. 연결된 사용자 권한이 다를 수 있습니다. 둘 다 영어의 의미에서 "보고서"이지만 매우 다릅니다.

다른 한편으로, 당신의 코드는 하나의 객체로 충분할 정도로 간단 할 수도 있지만, 코드에 고추가 들어간 경우 고려해야 할 사항입니다 if (validId) {...} else {...}.


0

아니요, 괜찮습니다! 다른 방법이없는 절차 적 언어로만 매개 변수를 수정하는 절차에 적합합니다. OOP 언어에서는 개체 (이 경우 보고서 (report.generateNewId ()와 같은))에서 변경 메서드를 호출합니다.

이 경우 메소드는 두 가지 작업을 수행하므로 SRP가 중단됩니다. 데이터베이스에 레코드를 삽입하고 새 ID를 생성합니다. 호출자는 메소드가 단순히 insertRecord ()라고하므로 새 ID를 생성한다는 것을 알 수 없습니다.


3
음 ... db.Reports.InsertOnSubmit(report)객체에서 change 메소드를 호출하면 어떻게됩니까?
Stephen C

괜찮습니다 ... 피해야하지만이 경우 LINQ to SQL은 매개 변수 수정을 수행하므로 OP가 후프 점프 클론 효과 (SRP 자체 위반)없이 피할 수는 없습니다.
Telastyn

@StephenC 보고서 객체에서 해당 메소드를 호출해야한다고 말하고 있습니다. 이 경우 매개 변수와 동일한 객체를 전달하는 것은 의미가 없습니다.
m3th0dman

@Telastyn 나는 일반적인 경우에 말하고 있었다; 좋은 관행은 100 % 존중 될 수 없습니다. 그의 특별한 경우에 아무도 5 줄의 코드에서 가장 좋은 방법을 추론 할 수는 없습니다.
m3th0dman
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.