Твердые принципы обеспечивают основу для написания высококачественного кода. Придерживаясь этих принципов, разработчики могут создавать эффективный, удобный в сопровождении, чистый и масштабируемый код. Вместо того, чтобы углубляться в теорию, давайте рассмотрим, как эти принципы можно применять на практике.

S — Принцип единой ответственности (SRP)

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

Давайте разберемся с кодом, здесь у нас есть класс, реализация которого показана в коде.

public class Users
{
    public string UserId { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
    public int ProductId { get; set; }
    public string ProductName { get; set; }

    public string ProductDescription { get; set; } public void AuthenticateUser(string _username, string _password)
    {
        //Code implementation to authenticate user
    }
    
    public void AddProduct(int _productId, string _productName, string         _productDescription)
    {
        //Code implementation to add product
    }
}

Здесь класс Пользователь содержит функциональные возможности, а также свойства Пользователя, а также Продукта, что не является масштабируемым и поддерживаемым подходом.

В соответствии с SRP мы разделяем функциональность Пользователя и Продукта, чтобы они не мешали функциональности друг друга, а также у класса есть единственная причина для изменения, когда есть дополнение или любое изменение, связанное с ним.

public class Users
{
    public string UserId { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
    
    public void AuthenticateUser(string _username, string _password)
    {
        //Code implementation to authenticate user
    }
}
public class Products
{
    public int ProductId { get; set; }
    public string ProductName { get; set; }

    public string ProductDescription { get; set; }public void AddProduct(int _productId, string _productName, string _productDescription)
    {
        //Code implementation to add product
    }
}

O — Принцип открытия/закрытия (OCP)

Принцип открытости/закрытости гласит, что классы или модули должны быть открыты для расширения, но закрыты для модификации. Другими словами, после создания класса или модуля его не следует изменять напрямую. Вместо этого новые функции следует добавлять с помощью нового кода, который расширяет существующий код.

Давайте проверим пример того, как работает OCP.

public static class ExtensionClass
{
	public static bool Rename(this FileInfo fileInfo, string newName)
	{
		try
		{
			fileInfo.MoveTo(newName);
		}
		catch (Exception)
		{
			return false;
		}
		
		return true;
	}
}

Мы добавили метод расширения в класс FileInfo, который доступен в пространстве имен System.IO и не содержит никакого метода с именем Rename, поэтому мы расширили класс FileInfo.

FileInfo file = new FileInfo("test.txt");
file.Rename("newname.txt");

Теперь доступ к методу Rename можно получить, создав объект FileInfo.

L — Принцип замещения Лисков (LSP)

Принцип подстановки Лисков гласит, что объекты суперкласса должны без проблем заменяться объектами подкласса.

Идея LSP состоит в том, чтобы сделать код более гибким и простым в обслуживании. Обеспечивая возможность взаимозаменяемости подклассов с их родительскими классами, мы можем писать более модульный и легко расширяемый код.

Давайте разберемся с этим на простом примере

public class SuperHero
{
	//Implementation of SuperHero
}

public class Batman : SuperHero
{
	//Implementation of Batman
}

Здесь у нас есть SuperHero и Batman, где SuperHero — это базовый класс, а Batman наследует свойства Супергерой. В соответствии с принципом замещения Лискова мы можем инициализировать SuperHero экземпляром Batman.

SuperHero super = new Batman();

I — Принцип разделения интерфейсов (ISP)

Принцип разделения интерфейса гласит, что ни один клиент не должен зависеть от методов, которые он не использует. Сегрегация простыми словами означает разделение.

Проще говоря, это означает, что при создании интерфейсов мы должны убедиться, что они сосредоточены на определенной функциональности и включают только методы, относящиеся к этой функциональности. От классов, реализующих интерфейс, не следует требовать реализации методов, которые им не нужны или которые им не нужны.

public interface IDemoInterface
{
	void AddUser();
	void DeleteUser();
	string GetUser(int UserId);
	void AddProduct();
	void DeleteProduct();
	string GetProduct(int ProductId);
	double GetPrice(int ProductId);
}

Здесь, в приведенном выше коде, если мы хотим внедрить IDemoInterface в наш класс с функциями управления пользователями, нам потребуется только некоторая реализация, а не другие ненужные методы, связанные с управлением продуктом. Таким образом, мы можем отделить/отделить интерфейс, поэтому мы реализуем соответствующую функциональность.

public interface IUser
{
	void AddUser();
	void DeleteUser();
	string GetUser(int UserId);
}
public interface IProduct
{
	void AddProduct();
	void DeleteProduct();
	string GetProduct(int ProductId);
	double GetPrice(int ProductId);
}

D — Принцип инверсии зависимостей (DIP)

Принцип инверсии зависимостей предполагает, что высокоуровневые модули не должны напрямую зависеть от низкоуровневых модулей, а вместо этого оба должны зависеть от абстракций.

Проще говоря, DIP — это разработка программного обеспечения таким образом, чтобы в будущем можно было легко вносить изменения. Абстрагируя детали реализации компонентов низкого уровня, изменения в этих компонентах не повлияют на компоненты высокого уровня, которые от них зависят. Это упрощает внесение изменений в систему без непредвиденных последствий.

Давайте разберемся на простом примере.

interface IDirectory
{
	List<string> GetDirectories();
}

public class FTPDirectory : IDirectory
{
	public List<string> GetDirectories()
	{
		//Implementation to get FTP Directories
	}
}

public class LocalDriveDirectory : IDirectory
{
	public List<string> GetDirectories()
	{
		//Implementation to get Local drive Directories
	}
}

Здесь у нас есть интерфейс, который реализуется двумя классами FTPDirectory и LocalDriveDirectory. В приведенном ниже коде мы внедряем зависимость с помощью конструктора и обращаемся к методам интерфейса IDirectory, который является абстракцией для низкоуровневых классов FTPDirectory и LocalDriveDirectory. сильный>.

public class DirectoryExplorer
{
	private readonly idirectory _directory;
        
        public directoryexplorer(idirectory directory)
	{
		_directory = directory;
	}
        
        public list<string> listdirectories()
	{
		return _directory.getdirectories();
	}
}

Используя принцип инверсии зависимостей, наш высокоуровневый класс не зависит напрямую от низкоуровневых классов, но зависит от абстракции. Это обеспечивает несвязанный, гибкий и модульный код. Мы можем создавать экземпляры Directory Explorer следующими способами в зависимости от наших требований.

DirectoryExplorer directoryExplorer = new DirectoryExplorer(new FTPDirectory());
DirectoryExplorer directoryExplorer = new DirectoryExplorer(new LocalDriveDirectory());

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

Если у вас есть какие-либо мысли или предложения о том, как мы можем улучшить наши сообщения в блоге, не стесняйтесь, дайте нам знать. Мы всегда ищем способы улучшить наш контент и сделать его более привлекательным для наших читателей. Если есть тема, которую вы хотели бы, чтобы мы осветили в будущем сообщении в блоге, мы будем рады услышать ваши идеи.

Вы можете оставить комментарий ниже или связаться с нами напрямую через наш веб-сайт, чтобы поделиться своими мыслями. Мы с нетерпением ждем от вас известий и продолжаем предоставлять ценный контент для наших читателей.