<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sarthak Kumar Shailendra</title>
    <description>The latest articles on DEV Community by Sarthak Kumar Shailendra (@sarthakkumarshailendra).</description>
    <link>https://dev.to/sarthakkumarshailendra</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1195364%2F7c015116-60e2-4311-8a9a-4b2687753fec.jpeg</url>
      <title>DEV Community: Sarthak Kumar Shailendra</title>
      <link>https://dev.to/sarthakkumarshailendra</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sarthakkumarshailendra"/>
    <language>en</language>
    <item>
      <title>SOLIDify Your Foundation: Mastering Software Design with a Deep Dive into SOLID Principles</title>
      <dc:creator>Sarthak Kumar Shailendra</dc:creator>
      <pubDate>Thu, 09 Nov 2023 14:58:50 +0000</pubDate>
      <link>https://dev.to/sarthakkumarshailendra/solidify-your-foundation-mastering-software-design-with-a-deep-dive-into-solid-principles-3ji2</link>
      <guid>https://dev.to/sarthakkumarshailendra/solidify-your-foundation-mastering-software-design-with-a-deep-dive-into-solid-principles-3ji2</guid>
      <description>&lt;p&gt;In the world of software development, writing maintainable and extensible code is crucial. One way to achieve this is by following the SOLID principles, a set of five design principles that help you create code that is more robust, easier to understand, and less prone to bugs. In this blog post, we'll explore each of the SOLID principles and provide code examples in Java to demonstrate how to apply them effectively.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgo8us5dmpreeon14zjj8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgo8us5dmpreeon14zjj8.png" alt="SOLID PRINCIPLES" width="800" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  - Single Responsibility Principle (SRP)
&lt;/h2&gt;

&lt;p&gt;The Single Responsibility Principle states that a class should have only one reason to change, meaning it should have only one responsibility. If a class has multiple responsibilities, changes in one area may inadvertently affect other areas of the code. Let's look at an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Item {
    private String description;
    private double price;
}

public class Order {
    private int orderId;
    private int quantity;
    private Item item;

    public Order(int orderId, Item item, int quantity) {
        this.orderId = orderId;
        this.item = item;
        this.quantity = quantity;
    }

    public double calculateTotal() {
        // Calculate the order total
        double orderTotal = item.price * this.quantity;
        return orderTotal;
    }

    public void saveToDatabase() {
        // Save the order to the database
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the Order class is responsible for two distinct tasks: calculating the order total and saving it to the database. This violates the SRP because the class has multiple reasons to change. This can lead to several problems such as&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Code Maintenance: If there are changes in the order calculation logic or the database operations, it can inadvertently affect the other part of the class. This design can lead to code that is difficult to maintain, test, and understand.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reusability: The order calculation and database operation logic are tightly coupled within the class, making it challenging to reuse either of these functionalities independently.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To adhere to SRP, we can separate these responsibilities into two different classes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Item {
    private String description;
    private double price;
}

public class Order {
    private int orderId;
    private int quantity;
    private Item item;

    public Order(int orderId, Item item, int quantity) {
        this.orderId = orderId;
        this.item = item;
        this.quantity = quantity;
    }

    public double calculateTotal() {
        // Calculate the order total
        double orderTotal = item.price * this.quantity;
        return orderTotal;
    }
}

public class OrderRepository {
    public void saveToDatabase(Order order) {
        // Save the order to the database
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the Order class is responsible only for order-related calculations, adhering to the SRP. The OrderRepository class is responsible for database operations, such as saving and retrieving orders. This separation of concerns not only makes the code easier to maintain and understand but also allows for independent testing and modification of each class without affecting the other.&lt;/p&gt;

&lt;h2&gt;
  
  
  - Open/Closed Principle (OCP)
&lt;/h2&gt;

&lt;p&gt;The Open/Closed Principle suggests that software entities (classes, modules, functions) should be open for extension but closed for modification. In other words, you should be able to add new functionality to your code without changing existing code. This principle encourages the use of inheritance and polymorphism to achieve extensibility.&lt;/p&gt;

&lt;p&gt;Let's illustrate the Open/Closed Principle with an example involving geometric shapes in Java:&lt;/p&gt;

&lt;p&gt;Consider a scenario where you're building a system to calculate and display the areas of various geometric shapes. Initially, you have a ShapeCalculator class that calculates the area of different shapes. However, as new shapes are introduced, the existing code must remain untouched.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class ShapeCalculator {
    public double calculateArea(Shape shape) {
        if (shape instanceof Circle) {
            Circle circle = (Circle) shape;
            return Math.PI * Math.pow(circle.getRadius(), 2);
        } else if (shape instanceof Rectangle) {
            Rectangle rectangle = (Rectangle) shape;
            return rectangle.getWidth() * rectangle.getHeight();
        }
        // Add more shapes and calculations...
        return 0.0;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this non-OCP example, to add a new shape, you would need to modify the ShapeCalculator class, violating the Open/Closed Principle.&lt;/p&gt;

&lt;p&gt;Now, let's apply the Open/Closed Principle to create an extensible design using inheritance and polymorphism:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;interface Shape {
    public double calculateArea();
}

class Circle implements Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public double calculateArea() {
        return Math.PI * Math.pow(radius, 2);
    }
}

class Rectangle implements Shape {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public double calculateArea() {
        return width * height;
    }
}

public class ShapeCalculator {
    public double calculateArea(Shape shape) {
        return shape.calculateArea();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you can easily add new shape types(e.g., Triangle, Square) without modifying the ShapeCalculator class.&lt;/p&gt;

&lt;h2&gt;
  
  
  - Liskov Substitution Principle (LSP)
&lt;/h2&gt;

&lt;p&gt;The Liskov Substitution Principle (LSP) is one of the SOLID principles of object-oriented design, and it emphasizes that objects of derived classes should be able to replace objects of the base class without affecting the correctness of the program. In simpler terms, if you have a base class and derived classes, the derived classes should be able to extend the base class(and not narrow it down) without changing the expected behaviour of the program.&lt;/p&gt;

&lt;p&gt;Let's illustrate LSP using a "Vehicle" example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Vehicle {
    public String getEngineName() {
        return "Vehicle Engine";
    }

    public int getNumberOfWheels() {
        return 2;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's create the "Car" class as a subclass of "Vehicle" to represent cars with engines and four wheels and let's also create a "Bicycle" class that extends the "Vehicle" class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Car extends Vehicle {
    @Override
    public String getEngineName() {
        return "Car Engine";
    }

    @Override
    public int getNumberOfWheels() {
        return 4;
    }
}

public class Bicycle extends Vehicle {
    // Bicycle-specific properties and methods

    @Override
    public int getNumberOfWheels() {
        return 2;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem here is that a bicycle doesn't have an engine, so the "getEngineName" method doesn't make sense for a bicycle (We can either choose to implement its own getEngineName function and return null or do not implement it at all and just inherit the generic getEngineName method).  If you create an instance of the "Bicycle" class and try to call "getEngineName", it could lead to confusion or unexpected behaviour in either case because bicycles don't have engines:&lt;/p&gt;

&lt;p&gt;Violation of Liskov Substitution Principle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Bicycle extends Vehicle {
    // Bicycle-specific properties and methods

    @Override
    public String getEngineName() {
        return null;
    }

    @Override
    public int getNumberOfWheels() {
        return 2;
    }
}

public class Main{
    public static void main(String[] args) {
        List&amp;lt;Vehicle&amp;gt; vehicleList = new ArrayList&amp;lt;&amp;gt;();
        vehicleList.add(new Vehicle());
        vehicleList.add(new Car());
        vehicleList.add(new Bicycle());

        for(Vehicle vehicle : vehicleList){
            System.out.println(vehicle.getEngineName());// This is not appropriate for a bicycle
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resolution - Applying Liskov Substitution Principle:&lt;/p&gt;

&lt;p&gt;To adhere to the Liskov Substitution Principle, you should modify the class hierarchy. One way to do this is by introducing an intermediate class, let's call it "EngineVehicle," which includes the "engineName" method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Vehicle {
    public int getNumberOfWheels() {
        return 2;
    }
}

public class EngineVehicle extends Vehicle {
    public String getEngineName() {
        return "Vehicle Engine";
    }
}

public class Car extends EngineVehicle {
    @Override
    public String getEngineName() {
        return "Car Engine";
    }

    @Override
    public int getNumberOfWheels() {
        return 4;
    }
}

public class Bicycle extends Vehicle {
    @Override
    public int getNumberOfWheels() {
        return 2;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the "Vehicle" class remains a base class without knowing anything about the engine, and the "EngineVehicle" class handles the engine-related methods.&lt;/p&gt;

&lt;p&gt;By restructuring the class hierarchy in this way, you ensure that the Liskov Substitution Principle is not violated. Now, you can use both "Vehicle" and "Bicycle" objects interchangeably without unexpected behaviour:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Main{
    public static void main(String[] args) {
        List&amp;lt;Vehicle&amp;gt; vehicleList = new ArrayList&amp;lt;&amp;gt;();
        vehicleList.add(new Vehicle());
        vehicleList.add(new Car());
        vehicleList.add(new Bicycle());

        for(Vehicle vehicle : vehicleList){
            System.out.println(vehicle.getNumberOfWheels());// This will work fine;
        }

        List&amp;lt;EngineVehicle&amp;gt; engineVehicleList = new ArrayList&amp;lt;&amp;gt;();
        engineVehicleList.add(new EngineVehicle());
        engineVehicleList.add(new Car());

        // and this is how we can print engine name
        for(EngineVehicle engineVehicle : engineVehicleList){
            System.out.println(engineVehicle.getEngineName();
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  - Interface Segregation Principle (ISP)
&lt;/h2&gt;

&lt;p&gt;The Interface Segregation Principle suggests that clients should not be forced to depend on interfaces they don't use. In other words, it's better to have several small, specific interfaces than one large, general interface. To illustrate the ISP, let's consider a restaurant employee scenario involving waiters and cooks.&lt;/p&gt;

&lt;p&gt;Imagine you're designing a software system for a restaurant management application, and you want to create interfaces for restaurant employees. In a poorly designed system without considering ISP, you might have a single monolithic interface that contains methods for all possible tasks employees could perform, such as serving customers, cooking food, cleaning tables, and managing reservations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public interface RestaurantEmployee {
    void takeOrder();
    void serveFood();
    void prepareFood();
    void handleCustomerPayment();
    void manageKitchen();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this scenario, you're violating the ISP because employees, such as cooks, should not be forced to implement methods like takeOrder or handleCustomerPayment that are irrelevant to their role. The same applies to waiters, who should not have to implement methods like prepareFood or manageKitchen.&lt;/p&gt;

&lt;p&gt;To adhere to the ISP, you should break down this monolithic interface into smaller, more specific interfaces that each represent a particular role or responsibility. Let's create separate interfaces for waiters and cooks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Interface for Waiters
public interface Waiter {
    void takeOrder();
    void serveFood();
    void handleCustomerPayment();
}

// Interface for Cooks
public interface Cook {
    void prepareFood();
    void manageKitchen();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the Waiter interface contains methods relevant to the responsibilities of waiters, while the Cook interface contains methods relevant to the responsibilities of cooks.&lt;/p&gt;

&lt;p&gt;With this design, employees can implement only the interfaces that match their specific roles. Waiters will implement the Waiter interface, and cooks will implement the Cook interface.&lt;/p&gt;

&lt;p&gt;This segregation ensures that each class is responsible for a focused set of tasks, making the codebase more maintainable and less error-prone. It also allows you to add new roles or responsibilities to your restaurant management system without affecting existing classes that don't need to implement them.&lt;/p&gt;

&lt;h2&gt;
  
  
  - Dependency Inversion Principle (DIP)
&lt;/h2&gt;

&lt;p&gt;The Dependency Inversion Principle (DIP) is one of the SOLID principles of object-oriented design. It states that high-level modules should not depend on low-level modules; both should depend on abstractions. In other words, it encourages the use of interfaces or abstract classes to define abstractions, allowing for flexibility and decoupling in your code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;According to DIP, our classes should depend upon interfaces or abstract classes instead of concrete classes and functions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's explain the Dependency Inversion Principle using an example of a keyboard:&lt;/p&gt;

&lt;p&gt;Imagine a typical computer system with a keyboard. In a system that doesn't follow the DIP, the high-level module (application) might directly depend on the low-level module (keyboard). This direct dependency can lead to inflexibility and complications when you want to switch or upgrade the keyboard.&lt;/p&gt;

&lt;p&gt;Non-DIP Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class Application {
    private Keyboard keyboard;

    // Direct dependency on Keyboard class.
    public Application(Keyboard keyboard) {
        this.keyboard = keyboard; 
    }

    public void typeMessage() {
        keyboard.type(); // The application directly uses the concrete keyboard class.
    }
}

public class Main{
    public static void main(String[] args) {
        Application application = new Application(new Keyboard()); //Directly instantiates the Keyboard class.

    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this non-DIP example, the Application class depends directly on the Keyboard class, which represents a specific type of keyboard. If you ever want to change the keyboard or use a different input device, you'd need to modify the Application class, violating the Open/Closed Principle (OCP).&lt;/p&gt;

&lt;p&gt;Now, let's apply the Dependency Inversion Principle:&lt;/p&gt;

&lt;p&gt;DIP Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public interface InputDevice {
    void type();
}

public class Keyboard implements InputDevice {
    public void type() {
        // Typing logic specific to the keyboard
    }
}

public class Application {
    private InputDevice inputDevice;

    public Application(InputDevice inputDevice) {
        this.inputDevice = inputDevice; // Accepts any input device that implements the InputDevice interface.
    }

    public void typeMessage() {
        inputDevice.type(); // The application uses the InputDevice interface, allowing flexibility.
    }
}

public class Main{
    public static void main(String[] args) {
        Application application = new Application(new Keyboard());

        Application application = new Application(new Mouse()); //In future we can pass any input device that implements the InputDevice interface without changing a single line in the Application class.
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this DIP-compliant example, we've introduced the InputDevice interface, which serves as an abstraction for any input device, such as a keyboard or a different input device. The Keyboard class implements this interface.&lt;/p&gt;

&lt;p&gt;The Application class now accepts an instance of an InputDevice through its constructor. This means you can easily swap out the concrete input device without modifying the Application class, adhering to the Open/Closed Principle (OCP) and providing flexibility. This separation of concerns and abstraction of dependencies makes the code more maintainable and extensible, aligning with the Dependency Inversion Principle (DIP).&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;SOLID principles are a cornerstone of writing clean, maintainable, and adaptable code. They provide a roadmap for creating software that can withstand the test of time, accommodate new features and requirements, and minimize the introduction of bugs during development and maintenance.&lt;/p&gt;

&lt;p&gt;By internalizing these principles and applying them consistently in your software development practices, you can build unshakable code that serves as a foundation for robust, long-lasting software systems. Embracing SOLID principles is not just a best practice but a commitment to quality and professionalism in the field of software development.&lt;/p&gt;

</description>
      <category>solidprinciples</category>
      <category>softwareengineering</category>
      <category>designpatterns</category>
      <category>java</category>
    </item>
    <item>
      <title>Docker Basics: A Beginner's Guide to Containerization</title>
      <dc:creator>Sarthak Kumar Shailendra</dc:creator>
      <pubDate>Fri, 27 Oct 2023 12:30:49 +0000</pubDate>
      <link>https://dev.to/sarthakkumarshailendra/docker-basics-a-beginners-guide-to-containerization-3e3p</link>
      <guid>https://dev.to/sarthakkumarshailendra/docker-basics-a-beginners-guide-to-containerization-3e3p</guid>
      <description>&lt;p&gt;In today's fast-paced world of software development and deployment, Docker has emerged as a revolutionary technology. Docker enables developers to package applications and their dependencies into lightweight, portable containers, making it easier to build, ship, and run applications consistently across different environments. Whether you're a seasoned developer or just getting started, this article will introduce you to the basics of Docker and help you understand why it's become an essential tool in the world of DevOps and software development.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Docker?
&lt;/h2&gt;

&lt;p&gt;Docker is an open-source platform designed to simplify the process of developing, shipping, and running applications. At its core, Docker relies on containerization, a technology that allows applications and their dependencies to be packaged together into isolated units known as containers. These containers are self-sufficient and can run consistently across different environments, be it your local development machine, a test server, or a production server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Docker?
&lt;/h2&gt;

&lt;p&gt;Docker offers several benefits that have made it immensely popular in the software development and deployment world:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Consistency: Containers ensure that an application runs the same way on any system, eliminating the classic "it works on my machine" problem.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Isolation: Each container encapsulates its application and dependencies, preventing conflicts and ensuring security.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Portability: Containers are lightweight and can be easily moved and executed on any platform or cloud provider that supports Docker.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Efficiency: Docker containers consume fewer resources compared to traditional virtual machines, making efficient use of system resources.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Version Control: Docker allows you to version your containers, which makes it easy to roll back to previous versions if needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scalability: Containers can be rapidly scaled up or down to accommodate changes in demand, facilitating efficient resource utilization.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Components of Docker
&lt;/h2&gt;

&lt;p&gt;Before you dive into Docker, it's essential to understand its key components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Docker Engine: This is the core component of Docker, responsible for running containers. It includes a server, an API, and a command-line interface (CLI) that you'll use to interact with Docker.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Images: Docker images are read-only templates that contain everything needed to run an application, including the code, libraries, and dependencies. Images are the basis for creating containers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Containers: Containers are instances of Docker images. They are lightweight, executable packages that include the application and its dependencies, isolated from the host system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dockerfile: A Dockerfile is a text file containing a set of instructions that define how to build a Docker image. It serves as the blueprint for creating an image.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Docker Registry: A Docker registry is a repository where Docker images are stored and shared. Docker Hub is the most popular public registry, but you can also set up private registries.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Getting Started with Docker
&lt;/h2&gt;

&lt;p&gt;Now that you have a basic understanding of Docker, let's walk through some practical steps to get you started:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Installation: Install Docker on your system by following the official installation guides for your specific operating system (Windows, macOS, or Linux).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hello, World!: Open a terminal and run your first Docker container by executing the following command.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will download the "hello-world" image from Docker Hub and create a container that prints a simple message.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker Images: Explore the available Docker images on Docker Hub (hub.docker.com). You can search for images and pull them to your local system using the docker pull command.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker pull nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will download the official Nginx web server image to your system.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating Your Own Docker Image: Create a simple web application or service and package it into a Docker image using a Dockerfile. You can then build and run this image as a container.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Use an official Node.js runtime as the base image
FROM node:21-alpine

# Set the working directory in the container
WORKDIR /usr/src/app

# Copy package.json and package-lock.json to the container. The syntax here is COPY &amp;lt;src&amp;gt; &amp;lt;dest&amp;gt;
COPY package*.json ./

# Install application dependencies
RUN npm install

# Copy the rest of the application source code to the container
COPY . .

# Expose a port for the application to listen on
EXPOSE 8080

# Define the command to run the application
CMD [ "node", "app.js" ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note that while RUN is executed in the container, COPY is executed on the host.&lt;/p&gt;

&lt;p&gt;You can then build this Docker image by navigating to the directory containing the Dockerfile and running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build -t my-node-app .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The -t flag tags the image with a name ("my-node-app" in this case). You can then run a container using your newly created image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -p 8080:8080 my-node-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This runs the application in a container and maps port 8080 from the container to your host system. &lt;/p&gt;

&lt;p&gt;Port mapping is typically defined using the -p (or --publish) option when starting a Docker container. The basic syntax is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -p HOST_PORT:CONTAINER_PORT my-container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;HOST_PORT: This is the port on your host system that you want to use for accessing the containerized service.&lt;/p&gt;

&lt;p&gt;CONTAINER_PORT: This is the port within the container where the service is running.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker Compose: As you work with more complex applications, Docker Compose helps manage multi-container applications. It allows you to define application services, networks, and volumes in a single YAML file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's an example of a docker-compose.yml file for a simple web application that uses a web server and a database. This file defines two services: one for the web application and one for the database, as well as networking and volume configurations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3'  # The version of the Docker Compose file format

services:
  # Service for the web application
  web:
    image: nginx:latest  # Use the official Nginx image from Docker Hub
    ports:
      - "8080:80"  # Map host port 8080 to container port 80
    volumes:
      - ./web-content:/usr/share/nginx/html  # Mount a local directory into the container
    networks:
      - my-network  # Attach to the custom network defined below

  # Service for the database
  db:
    image: postgres:latest  # Use the official PostgreSQL image from Docker Hub
    environment: # The environment section sets environment variables for the PostgreSQL container, specifying the username, password, and database name.
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: mypassword
      POSTGRES_DB: mydatabase
    volumes:
      - db-data:/var/lib/postgresql/data  # Create a named volume for data storage
    networks:
      - my-network

networks:
  my-network:  # Define a custom network
    driver: bridge  # Use the default bridge network driver

volumes:
  db-data:  # The volumes section creates a named volume named db-data to store the PostgreSQL data. This ensures that data persists across container restarts.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can start both services with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Learn Docker Commands: Familiarize yourself with essential Docker commands like docker ps to list running containers, docker stop to stop a container, docker logs to view container logs, and docker exec to execute commands within a running container.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Networking and Volumes: Understand Docker's networking model and how to use volumes to manage data persistence between containers and the host. Docker volumes are a feature that allows you to manage and persist data created by and used by Docker containers. Volumes are separate from the container's file system, and they provide a way to store and share data between containers and between the host system and containers. &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To create a Docker volume and mount it to a container, you can use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker volume create mydata
docker run -d -v mydata:/data my-node-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Docker has revolutionized the way software is developed, deployed, and managed. By containerizing applications and their dependencies, Docker provides a consistent and efficient platform for developers and operators. Whether you're building a small personal project or working in a large enterprise environment, Docker's flexibility and portability can help streamline your workflow. This article serves as a starting point on your Docker journey, and with practice, you'll unlock its full potential and reap the benefits of containerization in your software development projects.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>beginners</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
