2014-12-03 1 views
0

BaseService라는 기본 클래스가 있다고 가정 해 봅시다. 그러면 BaseService에서 상속되는 AuditService라는 또 다른 클래스가 있으며 BaseService로부터 상속받은 FooService라는 또 다른 클래스가있을 수 있습니다.여러 하위 개체로 상속을 구현하는 방법 .net

그래서 계층 지금이

BaseService 
| 
---- AuditService 
| 
---- FooService 

어떻게 생겼는지 내가 AuditService 및 FooService의 모든 기능을 포함하는 새로운 서비스를 필요로하는 경우?

# 여러 개체에서 상속을 허용하지 않으므로 어떻게해야합니까?

1 개의 서비스에서 상속하고 나머지 모든 항목을 다시 코딩하고 싶지는 않습니다.

편집 :

내 클래스에 대한 자세한 몇 가지 세부 사항 아래의 일부 코드를 포함합니다.

위에서 볼 수 있듯이 기본 서비스의 특정 기능을 foo 서비스 및 감사 대상 서비스에서 대체합니다. 앞으로는 baseervice, foo 서비스 및 감사 가능한 서비스의 모든 기능을 구현하는 서비스가 필요합니다.

BASESERVICE

public abstract class BaseService<TEntity> 
    where TEntity : class, IBaseEntity 
{ 
    protected DataContext _context; 

    protected BaseService(DataContext context) 
    { 
     _context = context; 
    } 

    public virtual async Task<ICollection<TEntity>> GetAllAsync() 
    { 
     return await _context.Set<TEntity>().ToListAsync(); 
    } 

    public virtual Task<TEntity> GetAsync(long id) 
    { 
     return _context.Set<TEntity>().FindAsync(id); 
    } 

    public virtual Task<int> AddAsync(TEntity t) 
    { 
     if (_context.Entry(t).State == EntityState.Detached) 
     { 
      _context.Set<TEntity>().Add(t); 
     } 

     _context.Entry(t).State = EntityState.Added; 

     return _context.SaveChangesAsync(); 
    } 

    public virtual Task<int> AddAllAsync(ICollection<TEntity> all) 
    { 
     foreach (var item in all) 
     { 
      if (_context.Entry(item).State == EntityState.Detached) 
      { 
       _context.Set<TEntity>().Add(item); 
      } 

      _context.Entry(item).State = EntityState.Added; 
     } 

     return _context.SaveChangesAsync(); 
    } 

    public virtual Task<int> UpdateAsync(TEntity updated) 
    { 
     _context.Entry(updated).State = EntityState.Modified; 

     return _context.SaveChangesAsync(); 
    } 

    public virtual async Task<int> DeleteAsync(long key) 
    { 
     TEntity entity = await GetAsync(key); 

     _context.Entry(entity).State = EntityState.Deleted; 

     return await _context.SaveChangesAsync(); 
    } 

    public virtual Task<int> DeleteAsync(TEntity t) 
    { 
     _context.Entry(t).State = EntityState.Deleted; 

     return _context.SaveChangesAsync(); 
    } 

} 

AUDITABLESERVICE

public class FooService<TEntity> : BaseService<TEntity> 
    where TEntity : class, IBaseEntity 
{ 
    protected IValidator<TEntity> _validator; 

    protected ValidatorService(DataContext context) 
     : base(context) 
    { 

    } 

    public override async Task<int> AddAsync(TEntity t) 
    { 
     var results = await _validator.ValidateAsync(t); 

     if (results.IsValid) 
     { 
      return await base.AddAsync(t); 
     } 
     else 
     { 
      throw new ValidationException(results.Errors); 
     } 
    } 

    public override async Task<int> UpdateAsync(TEntity updated) 
    { 
     var results = await _validator.ValidateAsync(updated); 

     if (results.IsValid) 
     { 
      return await base.UpdateAsync(updated); 
     } 
     else 
     { 
      throw new ValidationException(results.Errors); 
     } 
    } 

} 

답변

2

@Christos에 따르면, .net에는 다중 인터리빙이 지원되지 않으므로 필요에 맞는 솔루션을 얻으려는 의도를 설명해야합니다. 조성물은 예를 들어, 용액 일 수있다.AutodService 및 FooService 인스턴스에 대한 참조를 보유하는 BaseService에서 파생 된 다른 클래스를 작성하여 BaseService에 이미 새 서비스에 대한 좋은 인터페이스가있는 경우 솔루션이 될 수 있습니다. 위임을 제어 할 수있는 이점이 있습니다. 데코레이터 패턴과 마찬가지로 SRP (Single responsibility principle)를 따르는 청결한 청혼 일 것입니다. BaseService이 구체적인 서비스를 제공하는 모든 방법을 정의하기 때문에

은 당신이 머무르는 코드는 컴포지션 완벽하게 맞는 ... 우리에게 새로운 서비스가 정의 된 서비스를 다시 사용하는 방법에 대한 자세한 정보를 제공 :

public class NewService<T> : BaseService<T> 
{ 
    private readonly FooService<T> _fooService; 
    private readonly AuditableService<T> _auditableService; 
    public NewService (AuditableService<T> auditableService, FooService<T> fooService) 
    { 
     if(auditableService == null) 
      throw new ArgumentNullException("auditableService "); 

     if(fooService == null) 
      throw new ArgumentNullException("fooService"); 

     _auditableService = auditableService; 
     _fooService = fooService; 
    } 

    public override Task<int> AddAsync(T t) 
    { 
     return _fooService.UpdateAsync(t); 
    } 

    public override Task<int> DeleteAsync(T t) 
    { 
     return _auditableService.DeleteAsync(t); 
    } 
} 

UnitTesting 편집 전달 된 서비스는 (공통된 기본 기본 클래스를 가졌으므로) 조롱하여 새 서비스에서 호출되는지 확인할 수 있습니다. 단위 테스트보다 많은 상호 작용 테스트가 있지만 올바른 방법을 호출 할 수 있습니다. 새 서비스에 사전/사후 처리가없는 경우 새 서비스에 대한 추가 테스트가 필요하지 않습니다.

+0

클래스가 – Gillardo

+0

(좋은 이름)을 상속하는 방법을 보여주기 위해 몇 가지 코드를 추가했습니다. 이는 의미가 있습니다. 고맙습니다. 또 다른 질문입니다. 주제를 약간 벗어났습니다. 이 newService를 테스트하는 유닛은 NewService에서 "new"함수 만 테스트하면됩니다. 단위 테스트는 BaseService 기능을 별도로 테스트 할 것입니까? 나는 newservice에서 각 baseservice 함수를 다시 테스트해야하지 않겠습니까? – Gillardo

+1

validatorService와 fooService를 모두 사용해야하는 NewServices가 여러 개있는 경우 이렇게 변경됩니까? 아니면 단순히 위의 패턴과 두 서비스를 필요로하는 각각의 새로운 서비스를 사용하는 새로운 ValidateFooService를 생성해야합니까? 나는 각각의 AddAsync를 호출하여 각 새로운 서비스에서 fooService를 호출하고 싶지 않으므로 (그들 중 25 개가 될 수있다) – Gillardo

1

다중 상속이 .net 사용하는 클래스에서 지원되지 않습니다

public class AuditableService<TEntity> : BaseService<TEntity> 
    where TEntity : class, IAuditableEntity 
{ 

    public virtual override Task<int> DeleteAsync(long key) 
    { 
     return DeleteAsync(key, true); 
    } 

    public virtual async Task<int> DeleteAsync(long key, bool useFlag) 
    { 
     TEntity entity = await GetAsync(key); 

     return await DeleteAsync(entity, useFlag); 
    } 

    public virtual override Task<int> DeleteAsync(TEntity t) 
    { 
     return DeleteAsync(t, true); 
    } 

    public virtual Task<int> DeleteAsync(TEntity t, bool useFlag) 
    { 
     if (useFlag) 
     { 
      // flag item as deleted 
      t.IsDeleted = true; 

      return UpdateAsync(t); 
     } 
     else 
     { 
      return base.DeleteAsync(t); 
     } 
    } 
} 

FOOSERVICE. 하나의 클래스에서만 상속받을 수 있지만 원하는만큼 많은 인터페이스를 구현할 수 있습니다. 따라서 다중 상속을 구현하는 데 사용할 수있는 메커니즘은 여러 인터페이스를 구현하는 것입니다.

아래는 간단하다 : 이제

public class AuditService : BaseService 
{ } 

public class FooService : BaseService 
{ } 

당신이 클래스를 AuditServiceFooService이 모두 TEH 서비스를 원하는 경우, 당신은 인터페이스를 선언 할 수, 예를 들어, 당신이 원하는 한 다음 클래스에서 질문을 할 수있어 모든 것을 포함하는 IUnionInterfaceIUnionInterface

public class SuperService : IUnionInterface 
{ } 

구현하거나 다음 AuditServiceFooService, INonCommonFeatures과의 비 일반적인 기능을 만들 포함하는 인터페이스를 선언 할 수 귀하의 클래스는 BaseService에서 상속 받고 INonCommonFeatures을 구현하십시오.

public class SuperService : BaseService, INonCommonFeatures 
{ } 

이제는 INonCommonFeatures의 기능 만 구현하면됩니다.

+0

나를 잘못 설명해 주실 수 있습니까? 미리 감사드립니다. – Christos

+0

당신이 인터페이스를 구현한다고 말할 때 (나는 인터페이스가 무엇인지 알고있다.) 그러나 실제 메소드 코드를 인터페이스에 추가 할 수 없으므로 어떻게 문제를 해결할 수 있을까? 나는 지금 다른 게시물을보고 인터페이스에 확장 메소드를 사용한다고 말하면 이것이 무슨 뜻입니까? – Gillardo

+0

@ user2736022 인터페이스를 사용하여 해당 메소드를 구현하는 것을 의미합니다. 인터페이스에 메서드 구현을 추가 할 수 없다는 것을 알고 있습니다. 당신은 그것의 서명만을 정의 할 수 있습니다. 하지만 원하는 대부분의 코드가'BaseService'에 구현되어 있기 때문에'AutidService'와'FooService' 사이에는 공통적이지 않은 메소드 만 구현하면됩니다. – Christos

관련 문제