简体   繁体   English

类设计:允许将类既用作对象,又提供公共静态方法

[英]Class design: allow a class to be used both as an object and also supply public static methods

I have a silly, little class " FileSystemSize " which can be used both as an object and also via public, static methods. 我有一个愚蠢的小类“ FileSystemSize ”,它既可以用作对象,也可以通过公共静态方法使用。 The output is similar, but not identical in each case. 输出是相似的,但在每种情况下都不相同。

The class was intially static , but I added the possibility to initialize it as an object to allow extending with new " convenience methods " in future versions, without the need for a lot of parameter parsing. 该类最初是静态的 ,但我添加了将其初始化为对象的可能性,以允许在将来的版本中使用新的“ 便捷方法 ”进行扩展,而无需进行大量参数解析。 For example I have GetKBString() , GetMBString() , etc...methods to allow getting the file size conveniently formatted the way I want it (as a string). 例如,我有GetKBString()GetMBString()等方法,可让您方便地以我想要的方式格式化文件大小(作为字符串)。 Internally the class stores the file byte size as a double. 在内部,该类将文件字节大小存储为双精度。

I am a bit confused if this makes sense at all. 如果这完全有意义,我会感到困惑。 It seems like I should perhaps split this into a static version and an object version like Microsoft does for Directory and DirectoryInfo. 似乎我应该将其拆分为静态版本和对象版本,例如Microsoft用于Directory和DirectoryInfo的版本。 However it just seems easier to me to have this all in one place with a name that can't be mistaken - it should be clear what FileSystemSize does? 但是,对我来说,将所有这些都放在一个地方且名称不能误会似乎更容易-应该清楚FileSystemSize的作用是什么? Are there any implications for maintenance that I am not anticipating? 对维护我没有任何影响吗? What's that smell? 那是什么味道?

var mypath = @"C:\mypath";

var filesystemsize = new FileSystemSize(mypath);
string kilobytes = filesystemsize.GetKBString();
string megabytes = filesystemsize.GetMBString();
double bytes = filesystemsize.ByteSize;

double staticbytes = FileSystemSize.GetDirectoryBytesSize(new DirectoryInfo(mypath));
double statickilobytes = FileSystemSize.ConvertSize(staticbytes, "KB");

Look at it another way: Why are you putting String/Number/UI formatting methods in your FileSystemSize method? 换一种方式来看:为什么要在FileSystemSize方法中放入String / Number / UI格式化方法?

While it can be used in respect of files, this is a general piece of functionality that IMHO should be found elsewhere in a well organised library - just as Path functions are not part of the File or Directory classes in .net, I would put the "format a number" methods in a string or maths utils class. 尽管可以将其用于文件,但这是IMHO的常规功能,应该在组织良好的库中的其他位置找到–正如Path函数不是.net中File或Directory类的一部分一样,我将字符串或maths utils类中的“格式化数字”方法。

Separate the responsibilities of your objects and you may find there is no need to mix static and non-static members in cases like this. 将对象的职责分开,您可能会发现在这种情况下无需混合静态成员和非静态成员。

A good test: if you're asking yourself and us whether it is okay, there is a chance it is not. 一个很好的测试:如果您要问自己和我们是否还可以,则有可能不这样做。

It may not be natural for users of the class to have some methods accessible via class and others via object, especially when the second do not really require the instance properties of the class. 对于类的用户来说,使某些方法可以通过类访问而另一些方法可以通过对象访问是不自然的,尤其是当第二种方法实际上不需要类的实例属性时。 Chances are they will be confused and will be referring to like: "WTF this programmer did that?!". 他们很可能会感到困惑,并且会像这样说:“这个程序员做的是WTF吗?!”。

I suggest going with all instance methods if you like the possibility to extend the class, either with extension methods or via subclassing. 如果您希望通过扩展方法或通过子类扩展类,则建议使用所有实例方法。

Since you're not having a lot of state in FileSystemSize, isn't this the perfect candidate for extension methods? 由于FileSystemSize中没有很多状态,因此这不是扩展方法的理想之选吗?

I'd personally provide extensions to format numbers as file size strings and use an enum to specify how to format the file sizes: 我将亲自提供扩展名,以将数字格式化为文件大小字符串,并使用一个enum来指定如何格式化文件大小:

public static class FileSystemSize
{
    public static long GetDirectoryBytesSize(string path);
}

public static class NumberExtensions
{
    public static string FormatAsFileSize(
        this long fileSize, FileSizeStringFormat format);
}

public enum FileSizeStringFormat
{
    KiloByte,
    MegaByte,
}

If you're using C# 3.0, your intentions may be better expressed with extension methods and IFormatProviders. 如果您使用的是C#3.0,则可以通过扩展方法和IFormatProviders更好地表达您的意图。 In code, this could be an extension method on the FileInfo and DirectoryInfo ToString methods, so they would read something like this: 在代码中,这可能是FileInfo和DirectoryInfo ToString方法的扩展方法,因此它们将读取以下内容:

var directorySize = myDirectory.ToString("GB");
var fileSize = myFile.ToString("MB");

The above code feels more natural for what you are trying to do. 上面的代码对于您尝试执行的操作而言更为自然。

See how the following works for you. 请参阅以下内容。 Some of it will need to be tested (the recursive method DirectoryInfoExtender.GetDirectorySize comes to mind). 其中一些将需要测试(想到递归方法DirectoryInfoExtender.GetDirectorySize)。 If you need to be able to write statements like Console.WriteLine("{0:GB}", fileInfo) , you might also consider writing an IFormatProvider as well. 如果您需要能够编写类似Console.WriteLine("{0:GB}", fileInfo)类的语句,则还可以考虑编写IFormatProvider。

Also note that I have intentionally skimped on null checks and exception handling for these publically accessible methods. 还要注意,我故意跳过了这些公共可访问方法的空检查和异常处理。

public static class DirectoryInfoExtender
{
    public static string ToString(this DirectoryInfo d, string format, int fractionalDigits)
    {
        double fileSize = GetDirectorySize(d);
        return FileSizeConverter.GetFileSizeString(fileSize, format, fractionalDigits);
    }

    public static double GetDirectorySize(DirectoryInfo d)
    {    
        var files = d.GetFiles();
        var directories = d.GetDirectories();

        if(files.Length == 0 && directories.Length == 0)
        {
            return 0;
        }
        else
        {
            double size = 0;

            foreach(var file in files)
            {
                size += file.Length;
            }

            foreach(var directory in directories)
            {
                size += GetDirectorySize(directory);
            }
        }

        return size;
    }
}


public static class FileInfoExtender
{
    public static string ToString(this FileInfo f, string format, int fractionalDigits)
    {
        return FileSizeConverter.GetFileSizeString(f.Length, format, fractionalDigits);
    }
}

public class FileSizeConverter
{
    public static string GetFileSizeString(double fileSize, string format, int fractionalDigits)
    {
        long divisor;
        string sizeIndicator;

        switch(format.ToLower().Trim())
        {
            case "gb":
                divisor = (long)Math.Pow(2, 30);
                sizeIndicator = "gigabytes";
                break;
            case "mb":
                divisor = (long) Math.Pow(2, 20);
                sizeIndicator = "megabytes";
                break;
            case "kb":
                divisor = (long)Math.Pow(2, 10);
                sizeIndicator = "kilobytes";
                break;
            default:
                divisor = 1;
                sizeIndicator = "bytes";
                break;
        }

        return String.Format("{0:N" + fractionalDigits +"} {1}", fileSize / divisor, sizeIndicator);
    }
}

Standard smell is the usage of static methods - this makes it hard to maintain in case you use those methods all over your code. 标准气味是静态方法的使用-如果在整个代码中都使用这些方法,则很难维护。

Another smell imho is: class name is not clear about what it actually does. 另一个气味恕我直言是:类名不清楚其实际作用。 From your description it is meant to format data in which case I would mention it in the class name. 从您的描述开始,它的意思是格式化数据,在这种情况下,我会在类名中提到它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM