С# тестовое задание

Подскажите пожалуйста новичку. Есть тестовое задание по C#:

Case A job of 1000 images is going to be edited by a crew of 3 people:

  • P1: 1 image per 2 minutes
  • P2: 1 image per 3 minutes
  • P3: 1 image per 4 minutes How long will this job take in total?
    How many images will be edited by every person?

Scoring:

  • Correct answer and explain the necessary steps
  • Working code
  • It should work for any situation (amount of images, amount of people, individual speed)

Please focus on:

  • OOP implementation;
  • N-tier architecture implementation;
  • Unit testing implementation;
  • General programming rules and standards usage.

Есть код :

Подскажите что нужно еще сделать для того, чтобы проект был готовым для собеседования. Как сделать юнит тестирование?

public class Image
{
    public int Id { get; }
    public string Title { get; }
             
    public Image(int id, string title)
    {
        Id = id;
        Title = title;
    }
}

using System;
using System.Collections.Generic;

/// <summary>Рабочий.</summary>
public class Worker
{
    /// <summary>Имя рабочего.</summary>
    public string Name { get; }
                     
    /// <summary>Возвращает изображение монтируемое рабочим.
    /// Если <see langword="null"/>, то значит рабочий простаивает. </summary>
    public Image CurrentImage { get; private set; }
                     
    /// <summary>Делегат метода для получения рабочим изображения для обработки.</summary>
    private readonly Func<Image> getCurrentImage;
                     
    /// <summary>Время необходимое рабочему для обработки изображения.</summary>
    public int ProcessTime { get; }
                     
    /// <summary>Время затраченное рабочим на обработку текущего изображения.</summary>
    public int ProcessingTime { get; private set; }
                     
    // Мутабельный список изображений, уже обработанных рабочим.
    private readonly List<Image> processedIages = new List<Image>();
                     
    /// <summary>Иммутабельный список изображений, уже обработанных рабочим.</summary>
    public IReadOnlyList<Image> ProcessedIages;
                     
    public Worker(string name, Func<Image> getCurrentImage, int processTime)
    {
        Name = name;
        this.getCurrentImage = getCurrentImage;
        ProcessTime = processTime;
        ProcessedIages = processedIages.AsReadOnly();
    }
                     
    /// <summary>Количество интервалов в которые рабочий работал.</summary>
    public int CountWorkingInterval { get; private set; }
                     
    /// <summary>Метод выполнения следующего интервала.</summary>
    public void NextWorkingInterval()
    {
        if (CurrentImage != null)
        {
            ProcessingTime--;
            CountWorkingInterval++;
        }
                     
        if (ProcessingTime == 0)
        {
            if (CurrentImage != null)
                processedIages.Add(CurrentImage);
                     
            CurrentImage = getCurrentImage();
            if (CurrentImage != null)
                ProcessingTime = ProcessTime;
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
             

public class Brigade
{
    private readonly Queue<Image> images = new Queue<Image>();
    private readonly List<Worker> workers = new List<Worker>();
            
    private Image GetNextImage()
    {
        if (images.Count == 0)
            return null;
             
        return images.Dequeue();
    }

    public Brigade()
    {
        for (int i = 0; i < 1000; i++)
        {
            images.Enqueue(new Image(i, $"Изображение №{i + 1}"));
        }
             
        workers.Add(new Worker("Первый", GetNextImage, 2));
        workers.Add(new Worker("Второй", GetNextImage, 3));
        workers.Add(new Worker("Третий", GetNextImage, 4));
    }
             
    public bool NextWorkingInterval()
    {
        workers.ForEach(w => w.NextWorkingInterval());
             
        return images.Count != 0 || workers.Any(w => w.CurrentImage != null);
    }
             
    public void WorkStart()
    {
        while (NextWorkingInterval())
        {
            Console.WriteLine($"Картин в очереди: {images.Count}");
        }
             
        foreach (var worker in workers)
        {
            Console.WriteLine($"{worker.Name}: {worker.CountWorkingInterval} - {worker.ProcessedIages.Count}");
        }
    }
}

static void Main(string[] args)
{
    Brigade brigade = new Brigade();
    brigade.WorkStart();
}

код на C++. помогите переписать на C#

#include <iostream>
#include<stdio.h>
#include<cmath>
using namespace std;
class IMG_Edit{
public:
    IMG_Edit()
    {
        int img=1000;
    int time=0;
    int group_quantity=3;
    Person = new double[group_quantity];
    for (int i=0;i<group_quantity;i++)
    {
        Person[i]=i+2;
    }
    
    };
IMG_Edit(int img,int group){
    this -> img=img;
    time=0;
    group_quantity=group;
    Person=new double[group_quantity];
    for (int i=0;i<group_quantity;i++)
    {
        Person[i]=i+2;
    }
    
};
    void set_speed(int p_num,int speed){
        Person[p_num-1]=speed;
    }
void set_img(int i)
    {
    img=i;
};
    void set_group(int group){
        group_quantity=group;
        delete[]Person;
        Person=new double [group_quantity];
        for(int i=0;i<group_quantity;i++){
            Person[i]=i+2;
        }
        
    };
    void show_info()
    {
        double temp_img=img;
        double img_per_min=0;
        for(int i=0;i<group_quantity;i++){
            img_per_min+=(1/Person[i]);
        }
        while(temp_img>=0)
        {
            temp_img=img_per_min;
            time++;
        }
    };
    double temp;
    if (time>60)
    {
        cout<<"this job will take in total:"<<time/60<<"hours"<<time%60<<"minutes"<<endl;
    }
    else{
        cout<<"this job will take in total:"<<time<<"minutes"<<endl;
    }
    for (int i=0;i<group_quantity;i++)
    {
        cout<<"Person"<<i+1<<"edited:"<<temp<<"images"<<endl;
    }
    time=0;
    cout<<endl;
};
    ~ IMG_Edit(){
        delete []Person;
    }
private:
    double img;
    double*Person;
    int group_quantity;
    int time;
};
void Menu()
{
    cout<<"choose option"<<endl;
    cout<<"1. Calculate & display the result"<<endl;
    cout<<"2. set images quantity"<<endl;
    cout<<"3. set amount of people in the group"<<endl;
    cout<<"4. set individual speed"<<endl;
    cout<<"0. Quit"<<endl;
};

    
int main()
{
    int answer=0;
    system("cls");
    IMG_Edit obj;
    cout<<"\n result of the task"<<endl;
    cout<<"Conditions: 100 images, 3 persons, default speed"<<endl;
    obj.show_info();
    cout<<endl;
    cout<<"choose option"<<endl;
    cout<<"1. change conditions"<<endl;
    cout<<"2. explanation to the answer"<<endl;
    cout<<"0. Quit"<<endl;
    cin>>answer;
    while(answer<0||answer>2)
    {
        cout<<"you entered an invalid value! try again..."<<endl;
        cin>>answer;
        
    }
    if (answer==2)
    {
        system("cls");
        cout<<"\n 2. explanation to the answer"<<endl;
        cout<<"first the number of processed images in 1 minute"<<endl;
        cout<<"s=1/p(s-speed of one person in one minute, p - speed of editing one picture by one person)"<<endl;
        cout<<"then summarize the value of the speed of all people in one minute"<<endl;
        cout<<"Next, create a cycle, the condition of wich is the presence of unprocessed images"<<endl;
        cout<<"each iterations of the cycle, substract from the number of unprocessed images the value"<<endl;
        cout<<"of the number of edited images in one minute by all people and increase the value of time by one minute"<<endl;
        cout<<"after the end of cycle, show the time in a readable form(hh,mm)"<<endl;
        cout<<"\n and display the number of edited images by each person"<<endl;
        cout<<"change conditions"<<endl;
        cout<<"0. Quit"<<endl;
        cin>>answer;
    }
    if(answer==1)
    {
        int img, group, speed, person;
        system("cls");
        cout<<"\n 2. input conditions"<<endl;
        cout<<"amount of images - \b\b"<<endl;
        cin>>img;
        cout<<"amount of people - \b\b"<<endl;
        cin>>group;
        IMG_Edit obj(img,group);
        do
        {system("cls");
            Menu();
            cin>>answer;
            while(answer<0||answer>4)
            {
                cout<<"you entered an invalid value! try again..."<<endl;
                cin>>answer;
            }
            switch(answer)
            {case 1:
                {
                    system("cls");
                    cout<<"\n1. calculate and display result\n\n"<<endl;
                    obj.show_info();
                    cout<<endl;
                    system("pause");
                    break;
                }
                case 2:
                {
                    system("cls");
                    cout<<"\n2. set images quantity\n\n"<<endl;
                    cout<<"amount of images - \b\b"<<endl;
                    cin>>img;
                    system("cls");
                    obj.set_img(img);
                    cout<<"\nnumber of images changed \n\n"<<endl;
                    system("pause");
                    break;
                }
                case 3:{
                    system("cls");
                    cout<<"\n3. set amount of people in the group \n\n"<<endl;
                    cout<<"amount of people - \b\b"<<endl;
                    cin>>img;
                    system("cls");
                    obj.set_group(group);
                    cout<<"\nnumber of people changed \n\n"<<endl;
                    system("pause");
                    break;                }
                case 4:{
                    system("cls");
                    cout<<"\n4. set individual speed\n\n"<<endl;
                    cout<<"amount of images - \b\b"<<endl;
                    cout<<"input number of people in the group\b\b"<<endl;
                    cin>>person;
            }
                    system("cls");
                    cout<<"\n\n how many minutes person needs for editing one image - \b\b"<<endl;
                    cin>>speed;
                    system("cls");
                    obj.set_speed(person, speed);
                    cout<<"\n\n individual speed of "<<person<<"person changed \n\n"<<endl;
                    system("pause");
                    break;

            }
        default;
            break;
                    
    }
    }while
        (answer!=0);
}
system("pause");
return 0;
    }

Ответы (2 шт):

Автор решения: S.H.

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

самый быстрый обработает 0.5 картин в минуту

средний - 0.33 картины в минуту

медленный - 0.25 картинв минуту

Средняя пропускная спопобность конвеера - 1.08333 картины в минуту.

1000 картин будут обработаны за 1000/1.0833333 = 923 с чем то минут.

Теперь посмотрим, что произойдет за это время.

самый быстрый обработает 461 картину за 922 минуты.

средний обработает 307 картин за 921 минуту

самый медленный - 230 картин за 920 минут

Таким образом, они обработают 998 картин, и при этом финишируют внемного разное время.

И останется необработанными 2 картины.

В этом конкретном случае - безразлично, кому из рабочих отдать их на обработку.

Отдаём их на обработку любым двум рабочим - получаем общее время обработки 924 минуты.

Теперьосталось всё это формализовать для любых наборов интервалов времени, количества картин и рабочих :-)

Ну, и у Вас там юнит-тесты просят в задании.

А вообще - достойная задача, будет интересно узнать продолжение!

→ Ссылка
Автор решения: Yaroslav

Чем отличается ProcessTime и ProcessingTime? Не читая комментов не понять, а комментировать каждую строчку это антипаттерн.

Worker.ProcessTime процесс чего? Это время за которое он выполняет работу ProductionTime.

Worker.CurrentImage(текущая картинка работника)? Текущее фото работника? Семантически не вяжется, потому что CurrentImage, getCurrentImage и ProductionTime это не ответственность класса Worker.

Worker.NextWorkingInterval()... и сразу всё всем ясно (нет).

Func<Image> getCurrentImage это не ооп, а функциональное программирование. И только автор или тот кто изучил код поймет как это должно работать. А вот Worker.SetJob(ITask task); поймет любой просто прочитав.

ITask описывает работника выполняющего задание, время, статус выполнения, EventHandler выполнения и метод ITask.Update(int timeStep) вместо непонятного NextWorkingInterval(). EditedImage наследуется от ITask. Worker.SetJob(new EditedImage(Image)); и никакие комментарии не нужны, потому что всё человеческим языком.

Вместо processedIages, Tasks.

public readonly string Name;
public readonly int ProductionTime;
private List<ITask> _tasks;

public IEnumerable<ITask> Tasks => _tasks;
public int TasksCount => _tasks.Count;
public int TotalJobTime => _tasks.Select(t => t.Time).Sum();

// не обязательно
public LastTask => TasksCount > 0 ? _tasks[TasksCount-1] : null;
public bool IsFree => TasksCount > 0 ? LastTask.Status == TaskStatus.Compleated : true;

Соответственно при выдаче заданий Brigade, подписывается на событие завершения и выдает sender.Worker новое задание до тех пор пока они не закончатся.

На тему standards usage, загугли c# fields order. Сначала константы, статические и только для чтения, потом публичные и приватные поля, потом конструктор и только после него свойства!

В Brigade всё захардкодино, и не понятно почему этот класс так называется и в задаче казано:

It should work for any situation (amount of images, amount of people, individual speed)

У него должен быть конструктор с массивом рабочих(судя по названию класса) и методом выполнения массива работ Brigade.SetJubs(ITask[] tasks); и это его ответственность. Никаких Console.WriteLine в нем быть не должно. Только по завершению выполнения работ бригады можно написать Worker.TasksCount и Worker.TotalJobTime каждого работника, хотя время бригады, это время самого последнего по времени работника. Скорость каравана, это скорость самого медленного верблюда.

Юнит тест по сути задает конкретные параметры(описанные в задаче) и сравнивает их с прогнозируемыми результатами, выдавая детали того, что не сошлось если есть ошибки.

→ Ссылка