Дизайн класса:разрешить использование класса как объекта, а также предоставлять общедоступные статические методы

StackOverflow https://stackoverflow.com/questions/1384100

Вопрос

У меня есть глупый маленький класс»размер файловой системы" который можно использовать как как объект, так и через публичные статические методы.Результат аналогичен, но не идентичен в каждом случае.

Класс был изначально статический, но я добавил возможность инициализировать его как объект, чтобы можно было расширить его новыми "удобные методы" в будущих версиях без необходимости тщательного анализа параметров.Например, у меня есть ПолучитьKBString(), ПолучитьMBString(), и т. д. методы, позволяющие получить размер файла, удобно отформатированный так, как я хочу (в виде строки).Внутри класс сохраняет размер файла в байтах как двойной.

Я немного смущен, если это вообще имеет смысл.Кажется, мне следует разделить это на статическую версию и объектную версию, как это делает Microsoft для Directory и DirectoryInfo.Однако мне кажется, что проще собрать все это в одном месте с именем, которое нельзя ошибиться - должно быть понятно, что делает FileSystemSize?Существуют ли какие-либо последствия для технического обслуживания, которых я не ожидаю?Что за запах?

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");
Это было полезно?

Решение

Посмотрите на это по-другому:Почему вы помещаете методы форматирования String/Number/UI в свой метод FileSystemSize?

Хотя его можно использовать и для файлов, это общая часть функциональности, которую, ИМХО, следует найти в другом месте хорошо организованной библиотеки - точно так же, как функции Path не являются частью классов File или Directory в .net, я бы поместил методы «форматирования числа» в классе строк или математических утилит.

Разделите обязанности своих объектов, и вы обнаружите, что в подобных случаях нет необходимости смешивать статические и нестатические члены.

Другие советы

Хороший тест:если вы спрашиваете себя и нас, все ли в порядке, есть вероятность, что это не так.

Для пользователей класса может быть неестественно иметь доступ к некоторым методам через класс, а к другим через объект, особенно если для второго на самом деле не требуются свойства экземпляра класса.Скорее всего, они будут сбиты с толку и будут ссылаться на что-то вроде:«Какого черта этот программист сделал это?!».

Я предлагаю использовать все методы экземпляра, если вам нравится возможность расширения класса либо с помощью методов расширения, либо через создание подклассов.

Поскольку в FileSystemSize не так много состояний, разве это не идеальный кандидат для методов расширения?

Лично я бы предоставил расширения для форматирования чисел в виде строк размера файла и использовал бы 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,
}

Если вы используете C# 3.0, ваши намерения могут быть лучше выражены с помощью методов расширения и IFormatProviders.В коде это может быть метод расширения методов FileInfo и DirectoryInfo ToString, поэтому они будут читаться примерно так:

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

Приведенный выше код кажется более естественным для того, что вы пытаетесь сделать.

Посмотрите, как следующее работает для вас.Некоторые из них нужно будет протестировать (на ум приходит рекурсивный метод DirectoryInfoExtender.GetDirectorySize).Если вам нужно уметь писать такие утверждения, как Console.WriteLine("{0:GB}", fileInfo), вы также можете рассмотреть возможность написания IFormatProvider.

Также обратите внимание, что я намеренно ограничился проверками на null и обработкой исключений для этих общедоступных методов.

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);
    }
}

Стандартный запах — это использование статических методов — это затрудняет поддержку, если вы используете эти методы по всему коду.

Еще один запах, имхо:имя класса неясно, что он на самом деле делает.Судя по вашему описанию, оно предназначено для форматирования данных, и в этом случае я бы упомянул об этом в имени класса.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top