【问题标题】:hiding internal services from outside world to ensure the correct high-level service is being used [closed]向外界隐藏内部服务以确保使用正确的高级服务[关闭]
【发布时间】:2023-09-25 17:20:01
【问题描述】:

我正在开发一个电子商务网站。我有包含属性和照片的广告实体。属性写入数据库,照片存储在文件系统中。

我在我的基础设施项目中创建了一个WriterService,该服务负责保存广告......在后台它应该知道属性转到数据库,照片转到文件系统......但是这个细节是与外界无关...外界应使用WriterService保存广告。

这是我的作家服务:

public class WriterService
{
    private DbWriter _dbWriter;
    private IFileWriter _fileWriter;

    // I believe I need to change the constructor in order to achieve my goal
    public WriterService(DbWriter dbWriter, IFileWriter fileWriter)
    {
        _dbWriter = dbWriter;
        _fileWriter = fileWriter;
    }

    public void WriterSomething(string text, Stream image)
    {
        _dbWriter.Write(text);
        _fileWriter.Write(image);
    }
}

现在在我的基础设施层我有DbWriterFileWriter 的实现,DbWriter 看起来像这样:

public DbWriter
{
    public void Write(string text) {/* write text to DB */}
}

FileWriter 可以有不同的实现:

public interface IFileWriter
{
   void Write(Stream image);
}

照片可能会写入本地磁盘或 AWS S3 存储桶:

public DiskDriveWriter : IFileWriter
{
    public void Write(Stream image) {/* write image to Disk */}
}

public AWSCloudWriter : IFileWriter
{
    public void Write(Stream image) {/* write image to AWS */}
}

我想强制外部世界(我的解决方案中的其他项目)使用WriterService,所以如果他们想保存一些图像他们不应该直接使用AWSCloudWriter,他们总是必须通过@987654335 来执行此操作@。可以强制执行吗?


更新

为了避免一个很长的问题,我创建了this code review 来解释我要解决的问题。

【问题讨论】:

  • 那么,有什么问题呢?你删除了如果你创建这些类internal 你会得到一个编译时错误的声明。这是唯一可以远程理解的问题陈述或合法问题。那么你的问题是什么?您需要提供一个良好的minimal reproducible example,清楚地显示问题是什么,并准确解释该代码的作用,包括任何错误消息的确切文本,以及原因你想不出解决办法。
  • @HoomanBahreini 过于关注实现细节。抽象 writer 服务并将其公开。如果抽象和实现存在于同一个程序集中,则让该项目管理与 IoC 容器的注册。这样消费者只知道服务抽象而不知道它的依赖关系。
  • 顺便说一句:通常的方法是提供一个工厂类,其中接口类型(在本例中为IFileWriter)是公共的,但没有一个实现类。工厂类是公共的,并为外部代码提供了一种机制来创建IFileWriter 的实例,而无需知道实际类型。这可能适用于您的情况,但您没有提供任何细节让任何人都可以肯定地知道这一点,更不用说工厂类的具体外观了。
  • 我同意@PeterDuniho 的建议。
  • 旁注:目标似乎是让你的图书馆用户的生活更加艰难——虽然人们可以轻松地模拟一个测试接口,但用合理设计的类很难做到这一点(而且几乎不可能用什么这个问题似乎试图实现)

标签: c# interface encapsulation access-modifiers


【解决方案1】:

我使用适配器将中间类映射到最终的 AWS/磁盘写入器,从而对实施者隐藏它们。

所有这些代码都在一个单独的类库中

IFileWriter

    internal interface IFileWriter
    {
        void Write(string text);
    }

两个实现 IFileWriter 的类

    internal class AWSWriter : IFileWriter
    {
        public void Write(string text)
        {
            //Write to AWS
        }
    }
    internal class DiskDriveWriter : IFileWriter
    {
        public void Write(string text)
        {
            //Write to disk
        }
    }

适配器的抽象基类

    public abstract class AbstractFileWriterAdapter
    {
        internal IFileWriter FileWriter { get; set; }
    }

两个适配器,一个用于 AWS,另一个用于 DiskWriter

    public class AWSFileWriterAdapter: AbstractFileWriterAdapter
    {
        public AWSFileWriterAdapter()
        {
            FileWriter = new AWSWriter();
        }
    }
    public class DiskDriveWriterAdapter:AbstractFileWriterAdapter
    {
        public DiskDriveWriterAdapter()
        {
            FileWriter = new DiskDriveWriter();
        }
    }

作家服务

public class WriterService
{
    AbstractFileWriterAdapter _writer;

    public WriterService(AbstractFileWriterAdapter writer)
    {
        _writer = writer;
    }

    public void WriteMessage(string text)
    {
        _writer.FileWriter.Write(text);
    }
}

最后来自不同项目的调用

            var awsAdapter = new AWSFileWriterAdapter();
            var service1 = new WriterService(awsAdapter);
            service1.WriteMessage("Some fancy text to AWS!!");


            var diskDriveAdapter = new DiskDriveWriterAdapter();
            var service2 = new WriterService(diskDriveAdapter);
            service2.WriteMessage("Some text to the drive!!");

【讨论】:

  • 这似乎是一个好方法。这与适配器设计模式有关吗?
  • 是的,总体思路是一样的。
  • 那么您是否遵循这种方法 - 只是好奇。
  • 这是一个有趣的方法,感谢您的回答...我想确保外部世界不会访问 WriterService 的内部(但这是不可能的,因为需要这些内部服务在构造函数参数中)...所以我需要重新考虑我的设计。
  • 是的,我明白了。 WriterService 需要一种方法来调用抽象的 Write 方法及其传入的决定如何实现它的类。祝你好运,我很想看到最终结果!
【解决方案2】:

如果你把IFileWriterinternalWriterService的构造函数设置为internal,它会起作用。

internal interface IFileWriter
internal WriterService(IFileWriter fileWriter)

类似这样的:

internal interface IWriter
{
    void Write(string text);
}

internal class WriterB : IWriter
{
    public void Write(string text) { Console.WriteLine($"A is writing '{text}'"); }
}

internal class WriterA : IWriter
{
    public void Write(string text) { Console.WriteLine($"B is writing '{text}'"); }
}

public class WriterService
{
    private readonly IWriter x;

    internal WriterService(IWriter x) { this.x = x; }

    public void Write(string text) { x.Write(text); }

    public static WriterService WithA() { return new WriterService(new WriterA()); }

    public static WriterService WithB() { return new WriterService(new WriterB()); }
}


public class Program
{
    public static void Main(string[] args)
    {
        var s = new WriterService(new WriterA());

        s.Write("Hello!");

        WriterService.WithA().Write("Hello again!");

        WriterService.WithB().Write("And again!");
    }
}

【讨论】:

  • 你如何在你的例子中实例化WriterService?假设它被它所在的程序集之外的一个类使用(可能是一个库等)......
  • 这里internal访问修饰符有什么意义?您只是在使用构造函数依赖注入...
  • 您可以使用工厂方法启动WriterService。 (添加示例)
  • @CoolBots 由于实现类型是内部的(OP 的愿望),构造函数也必须是内部的。
  • @tymtam:感谢您的回答....但现在 A 级仍然可以从外部世界看到。我想看看是否可以通过WriterService强制外部世界访问A类
最近更新 更多