2014-12-03 53 views
0

說我有一個基類,稱爲BaseService,然後我有另一個名爲AuditService的類,它從BaseService繼承,也許我有另一個名爲FooService,也從BaseService繼承的類。如何實現與多個子對象的繼承.net

所以層次結構看起來像這樣

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

現在,如果我需要一個包含AuditService和FooService接口的所有功能,一個新的服務?

c#不允許你從多個對象繼承,所以我會怎麼做呢?

我不想從1個服務繼承,並重新編碼所有其餘的項目。

編輯:

要包括有關我的班一些細節,下面的一些代碼。

正如你所看到的,我重寫了基本服務在foo服務和可審計服務中的特定功能。在將來,我將需要一個服務,實現baseservice,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 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 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中不存在多重互聯支持 - 您必須描述您的意圖以獲得適合您需求的解決方案。

組合物可以是一種解決方案,例如,創建另一個派生自BaseService的類,該類持有對AutidService和FooService實例的引用,以將操作委派給它們(如果BaseService已經爲您的新服務提供了良好的接口,那麼這將是一個解決方案。您可以控制委派像裝飾者模式一樣,可能這將是SRP(單一職責原則)之後的一個清晰的注意事項)。

給我們介紹一下如何在新的服務應該重新使用已定義服務的詳細信息...

你添加的代碼符合完美的組合,因爲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); 
    } 
} 

編輯單元測試 您可以模擬傳遞的服務(因爲它們有一個共同的抽象基類)並驗證它們是否被新服務調用。這比單元測試更多的交互測試,但它會確保你調用正確的方法。如果新服務中沒有預處理/後處理,則不需要對新服務進行任何進一步測試。

+0

我已經添加了一些代碼來向您展示類是如何繼承 – Gillardo 2014-12-03 11:06:28

+0

(好名字),這是有道理的。謝謝。另一個問題,如果你不介意的話,稍微偏離主題。單元測試這個新服務,我猜測我只需要在NewService中測試「新」功能,單元測試會分別測試BaseService功能。我wouldnt必須重新測試每個基本服務功能從newservice我會嗎? – Gillardo 2014-12-03 12:06:43

+1

如果我有多個NewServices應該同時使用validatorService和fooService,會發生這種變化嗎?或者,我應該簡單地創建一個使用上述模式的新ValidateFooService,並且每個需要這兩個服務的新服務都只是繼承它?因爲我不想重新編碼每個AddAsync在我創建的每個新服務中調用fooService(可能是其中的25個) – Gillardo 2014-12-03 13:32:20

1

多重繼承不.net使用類的支持。您只能從一個類繼承,但您可以根據需要實現儘可能多的接口。因此,可以用來實現多重繼承的機制是使用實現多個接口。

下面是簡單的:

public class AuditService : BaseService 
{ } 

public class FooService : BaseService 
{ } 

現在,如果你想有一個類的所有格蘭服務AuditServiceFooService有,你可以聲明的接口,例如IUnionInterface,在其中將包含所有你想要,然後你可以要求從你的類實現IUnionInterface

public class SuperService : IUnionInterface 
{ } 

,或者你可以聲明包含AuditServiceFooServiceINonCommonFeatures然後非共同特徵將界面你的班級繼承你的BaseService並執行INonCommonFeatures

public class SuperService : BaseService, INonCommonFeatures 
{ } 

現在您只需實現INonCommonFeatures的功能。

+0

可以請downvoter解釋我在哪裏我錯了?先謝謝你。 – Christos 2014-12-03 10:56:46

+0

當你說實現一個接口是什麼意思(我知道接口是什麼),但你不能添加實際的方法代碼到接口,那麼這將如何解決這個問題?我現在已經看過其他文章,他們說在接口上使用擴展方法,這是你的意思嗎? – Gillardo 2014-12-03 11:02:24

+0

@ user2736022我的意思是使用一個接口實現相應的方法。我知道你不能在界面中添加方法的實現。你只能定義它的簽名。但是因爲你想要的大部分代碼都是在'BaseService'中實現的,所以你只需要實現'AutidService'和'FooService'之間不常見的方法。 – Christos 2014-12-03 12:04:05